/*****************************************************************************
 * Copyright (C) 2016 MediaTek Inc.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See http://www.gnu.org/licenses/gpl-2.0.html for more details.
 *
 * Accelerometer Sensor Driver
 *
 * This software is licensed under the terms of the GNU General Public
 * License version 2, as published by the Free Software Foundation, and
 * may be copied, distributed, and modified under those terms.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 *
 *****************************************************************************/

/*****************************************************************************
 *** HEADER FILES
 *****************************************************************************/

#include "cust_acc.h"
#include "accel.h"
#include "mc3433.h"


/*****************************************************************************
 *** CONFIGURATION
 *****************************************************************************/
/* Just for mcube's calibration APK*/
/* #define _MC3XXX_SUPPORT_DOT_CALIBRATION_  */
#define _MC3XXX_SUPPORT_LPF_
/*#define _MC3XXX_SUPPORT_CONCURRENCY_PROTECTION_ */
/* #define _MC3XXX_SUPPORT_APPLY_AVERAGE_AGORITHM_ */
/* #define _MC3XXX_SUPPORT_PERIODIC_DOC_ */
/* #define _MC3XXX_SUPPORT_VPROXIMITY_SENSOR_ */
#define _MC3XXX_SUPPORT_LRF_
/* #define _MC3XXX_SUPPORT_POWER_SAVING_SHUTDOWN_POWER_ */
#define C_MAX_FIR_LENGTH	(32)
#define VIRTUAL_Z	0

/*****************************************************************************
 *** CONSTANT / DEFINITION
 *****************************************************************************/
/**************************
 *** CONFIGURATION
 **************************/
#define MC3XXX_DEV_NAME						"MC3XXX"
#define MC3XXX_DEV_DRIVER_VERSION			  "2.1.6"
#define MC3XXX_DEV_DRIVER_VERSION_VIRTUAL_Z	"1.0.1"

/**************************
 *** COMMON
 **************************/
#define MC3XXX_AXIS_X	  0
#define MC3XXX_AXIS_Y	  1
#define MC3XXX_AXIS_Z	  2
#define MC3XXX_AXES_NUM	3
#define MC3XXX_DATA_LEN	6
#define MC3XXX_RESOLUTION_LOW	 1
#define MC3XXX_RESOLUTION_HIGH	2
#define MC3XXX_LOW_REOLUTION_DATA_SIZE	 3
#define MC3XXX_HIGH_REOLUTION_DATA_SIZE	6
#define MC3XXX_INIT_SUCC	(0)
#define MC3XXX_INIT_FAIL	(-1)
#define MC3XXX_REGMAP_LENGTH	(64)
#define C_I2C_FIFO_SIZE	 8

#define ACC_TAG		"<ACCELEROMETER> "
#define ACC_LOG(fmt, args...)		pr_debug(ACC_TAG"%s %d : "\
		fmt, __func__, __LINE__, ##args)
#define ACC_PR_ERR(fmt, args...)	pr_err(ACC_TAG"%s %d : "\
		fmt, __func__, __LINE__, ##args)

/* Maintain  cust info here */
struct acc_hw accel_cust;
static struct acc_hw *hw = &accel_cust;

/* For  driver get cust info */
struct acc_hw *get_cust_acc(void)
{
	return &accel_cust;
}
/*****************************************************************************
 *** DATA TYPE / STRUCTURE DEFINITION / ENUM
 *****************************************************************************/
enum MCUBE_TRC {
	MCUBE_TRC_FILTER  = 0x01,
	MCUBE_TRC_RAWDATA = 0x02,
	MCUBE_TRC_IOCTL   = 0x04,
	MCUBE_TRC_CALI	= 0X08,
	MCUBE_TRC_INFO	= 0X10,
	MCUBE_TRC_REGXYZ  = 0X20,
};

struct scale_factor {
	u8	whole;
	u8	fraction;
};

struct data_resolution {
	struct scale_factor	scalefactor;
	int					sensitivity;
};

struct data_filter {
	s16	raw[C_MAX_FIR_LENGTH][MC3XXX_AXES_NUM];
	int	sum[MC3XXX_AXES_NUM];
	int	num;
	int	idx;
};

struct mc3xxx_i2c_data {
	/* ================================================ */
	struct i2c_client *client;
	struct acc_hw *hw;
	struct hwmsen_convert cvt;

	/* ================================================ */
	struct data_resolution *reso;
	atomic_t trace;
	atomic_t suspend;
	atomic_t selftest;
	atomic_t filter;
	s16	cali_sw[MC3XXX_AXES_NUM + 1];

	/* ================================================ */
	s16	offset[MC3XXX_AXES_NUM + 1];
	s16	data[MC3XXX_AXES_NUM + 1];

	/* ================================================ */
	#if defined(_MC3XXX_SUPPORT_LPF_)
	atomic_t firlen;
	atomic_t fir_en;
	struct data_filter fir;
	#endif
};

#ifdef _MC3XXX_SUPPORT_LRF_
struct S_LRF_CB {
	s16	nIsNewRound;
	s16	nPreDiff;
	s16	nPreValue;
	s16	nMaxValue;
	s16	nMinValue;
	s16	nRepValue;
	s16	nNewDataMonitorCount;
};
#endif

/*****************************************************************************
 *** EXTERNAL FUNCTION
 *****************************************************************************/
/* extern struct acc_hw*	mc3xxx_get_cust_acc_hw(void); */

/*****************************************************************************
 *** STATIC FUNCTION
 *****************************************************************************/
static int mc3xxx_i2c_probe(struct i2c_client *client,
	const struct i2c_device_id *id);
static int mc3xxx_i2c_remove(struct i2c_client *client);
static int _mc3xxx_i2c_auto_probe(struct i2c_client *client);
static int mc3xxx_suspend(struct device *dev);
static int mc3xxx_resume(struct device *dev);
static int mc3xxx_local_init(void);
static int mc3xxx_remove(void);
static int MC3XXX_SetPowerMode(struct i2c_client *client, bool enable);
static int MC3XXX_WriteCalibration(struct i2c_client *client,
	int dat[MC3XXX_AXES_NUM]);
static void MC3XXX_SetGain(void);

/*****************************************************************************
 *** STATIC VARIABLE & CONTROL BLOCK DECLARATION
 *****************************************************************************/
static unsigned char	s_bResolution;
static unsigned char	s_bPCODE;
static unsigned char	s_bPCODER;
static unsigned char	s_bHWID;
static unsigned char	s_bMPOL;
static int	s_nInitFlag = MC3XXX_INIT_FAIL;
static struct acc_init_info  mc3xxx_init_info = {
	.name   = MC3XXX_DEV_NAME,
	.init   = mc3xxx_local_init,
	.uninit = mc3xxx_remove,
	};
#ifdef CONFIG_OF
static const struct of_device_id accel_of_match[] = {
	{.compatible = "mediatek,gsensor"},
	{},
};
#endif
static const struct i2c_device_id mc3xxx_i2c_id[] = {
	{MC3XXX_DEV_NAME, 0},
	{},
};
static unsigned short mc3xxx_i2c_auto_probe_addr[] = {
	0x4C, 0x6C, 0x4E, 0x6D, 0x6E, 0x6F
};
#ifdef CONFIG_PM_SLEEP
static const struct dev_pm_ops mc3xxx_pm_ops = {
	SET_SYSTEM_SLEEP_PM_OPS(mc3xxx_suspend, mc3xxx_resume)
};
#endif
static struct i2c_driver	mc3xxx_i2c_driver = {
	.driver = {
		.name = MC3XXX_DEV_NAME,
#ifdef CONFIG_PM_SLEEP
		.pm = &mc3xxx_pm_ops,
#endif
	#ifdef CONFIG_OF
		.of_match_table = accel_of_match,
	#endif
	},
	.probe  = mc3xxx_i2c_probe,
	.remove = mc3xxx_i2c_remove,
	.id_table = mc3xxx_i2c_id,
	};

static struct i2c_client		*mc3xxx_i2c_client;
static struct mc3xxx_i2c_data   *mc3xxx_obj_i2c_data;
static struct data_resolution	mc3xxx_offset_resolution = { {7, 8}, 256 };
static bool	mc3xxx_sensor_power;
static struct GSENSOR_VECTOR3D	gsensor_gain;
static char	selftestRes[10] = {0};
static struct file *fd_file;
static mm_segment_t oldfs;
static unsigned char offset_buf[6];
static signed int	offset_data[3];
static signed int	gain_data[3];
static unsigned char	s_baOTP_OffsetData[6] = { 0 };
static signed int	s_nIsRBM_Enabled;
static DEFINE_MUTEX(MC3XXX_i2c_mutex);

#ifdef _MC3XXX_SUPPORT_LRF_
	static struct S_LRF_CB	s_taLRF_CB[MC3XXX_AXES_NUM];
#endif

#ifdef _MC3XXX_SUPPORT_CONCURRENCY_PROTECTION_
	static struct semaphore	s_tSemaProtect;
#endif

#ifdef _MC3XXX_SUPPORT_DOT_CALIBRATION_
	static int	s_nIsCaliLoaded;
	static int	LPF_FirstRun = 1;
#endif

static int			 LPF_SamplingRate = 5;
static int			 LPF_CutoffFrequency = 0x00000004;
static unsigned int	iAReal0_X;
static unsigned int	iAcc0Lpf0_X;
static unsigned int	iAcc0Lpf1_X;
static unsigned int	iAcc1Lpf0_X;
static unsigned int	iAcc1Lpf1_X;

static unsigned int	iAReal0_Y;
static unsigned int	iAcc0Lpf0_Y;
static unsigned int	iAcc0Lpf1_Y;
static unsigned int	iAcc1Lpf0_Y;
static unsigned int	iAcc1Lpf1_Y;

static unsigned int	iAReal0_Z;
static unsigned int	iAcc0Lpf0_Z;
static unsigned int	iAcc0Lpf1_Z;
static unsigned int	iAcc1Lpf0_Z;
static unsigned int	iAcc1Lpf1_Z;

static signed char	s_bAccuracyStatus = SENSOR_STATUS_ACCURACY_MEDIUM;


#ifdef _MC3XXX_SUPPORT_PERIODIC_DOC_
	static DECLARE_WAIT_QUEUE_HEAD(wq_mc3xxx_open_status);

	static atomic_t	s_t_mc3xxx_open_status = ATOMIC_INIT(0);

	static unsigned char	s_bIsPDOC_Enabled;
#endif

#ifdef _MC3XXX_SUPPORT_VPROXIMITY_SENSOR_
	static int	P_STATUS;
	static int	prev_P_STATUS;
#endif

/*****************************************************************************
 *** MACRO
 *****************************************************************************/
#ifdef _MC3XXX_SUPPORT_CONCURRENCY_PROTECTION_
static int mc3xxx_mutex_lock(void);
static void mc3xxx_mutex_unlock(void);
static void mc3xxx_mutex_init(void);

static void mc3xxx_mutex_init(void)
{
	sema_init(&s_tSemaProtect, 1);
}

static int mc3xxx_mutex_lock(void)
{
	if (down_interruptible(&s_tSemaProtect))
		return (-ERESTARTSYS);
	return 0;
}

static void mc3xxx_mutex_unlock(void)
{
	up(&s_tSemaProtect);
}
#else
	#define mc3xxx_mutex_init()				do {} while (0)
	#define mc3xxx_mutex_lock()				do {} while (0)
	#define mc3xxx_mutex_unlock()			  do {} while (0)
#endif

#define IS_MCFM12()	((s_bHWID >= 0xC0) && (s_bHWID <= 0xCF))
#define IS_MCFM3X()	((s_bHWID == 0x20)\
	|| ((s_bHWID >= 0x22) && (s_bHWID <= 0x2F)))

/*****************************************************************************
 *** TODO
 *****************************************************************************/
#define DATA_PATH			"/sdcard2/mcube-register-map.txt"

#ifdef _MC3XXX_SUPPORT_DOT_CALIBRATION_
	static char	file_path[MC3XXX_BUF_SIZE]	=
			"/data/data/com.mcube.acc/files/mcube-calib.txt";
	static char	backup_file_path[MC3XXX_BUF_SIZE] =
			"/data/misc/sensors/mcube-calib.txt";
#endif

/*****************************************************************************
 *** FUNCTION
 *****************************************************************************/

/**************I2C operate API*****************************/
static int MC3XXX_i2c_read_block(struct i2c_client *client, u8 addr,
	u8 *data, u8 len)
{
	u8 beg = addr;
	int err;
	struct i2c_msg msgs[2] = {{0}, {0} };

	mutex_lock(&MC3XXX_i2c_mutex);

	msgs[0].addr = client->addr;
	msgs[0].flags = 0;
	msgs[0].len = 1;
	msgs[0].buf = &beg;

	msgs[1].addr = client->addr;
	msgs[1].flags = I2C_M_RD;
	msgs[1].len = len;
	msgs[1].buf = data;

	if (!client) {
		mutex_unlock(&MC3XXX_i2c_mutex);
		return -EINVAL;
	} else if (len > C_I2C_FIFO_SIZE) {
		ACC_PR_ERR(" length %d exceeds %d\n", len, C_I2C_FIFO_SIZE);
		mutex_unlock(&MC3XXX_i2c_mutex);
		return -EINVAL;
	}
	err = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
	if (err != 2) {
		ACC_PR_ERR("i2c_transfer error: (%d %p %d) %d\n", addr, data,
			len, err);
		err = -EIO;
	} else
		err = 0;

	mutex_unlock(&MC3XXX_i2c_mutex);
	return err;

}

static int MC3XXX_i2c_write_block(struct i2c_client *client, u8 addr,
	u8 *data, u8 len)
{
	/* address occupies one byte, the max length for write is 7 bytes */
	int err, idx, num;
	char buf[C_I2C_FIFO_SIZE];

	err = 0;
	mutex_lock(&MC3XXX_i2c_mutex);
	if (!client) {
		mutex_unlock(&MC3XXX_i2c_mutex);
		return -EINVAL;
	} else if (len >= C_I2C_FIFO_SIZE) {
		ACC_PR_ERR(" length %d exceeds %d\n", len, C_I2C_FIFO_SIZE);
		mutex_unlock(&MC3XXX_i2c_mutex);
		return -EINVAL;
	}

	num = 0;
	buf[num++] = addr;
	for (idx = 0; idx < len; idx++)
		buf[num++] = data[idx];

	err = i2c_master_send(client, buf, num);
	if (err < 0) {
		ACC_PR_ERR("send command error!!\n");
		mutex_unlock(&MC3XXX_i2c_mutex);
		return -EFAULT;
	}
	err = 0;

	mutex_unlock(&MC3XXX_i2c_mutex);
	return err;
}


/*****************************************
 *** GetLowPassFilter
 *****************************************/
static unsigned int GetLowPassFilter(unsigned int X0, unsigned int Y1)
{
	unsigned int lTemp;

	lTemp = Y1;
	lTemp *= LPF_CutoffFrequency;	/* 4HZ LPF RC=0.04 */
	X0 *= LPF_SamplingRate;
	lTemp += X0;
	lTemp += LPF_CutoffFrequency;
	lTemp /= (LPF_CutoffFrequency + LPF_SamplingRate);
	Y1 = lTemp;

	return Y1;
}

/*****************************************
 *** openFile
 *****************************************/
static struct file *openFile(char *path, int flag, int mode)
{
	struct file *fp = NULL;

	fp = filp_open(path, flag, mode);

	if (IS_ERR(fp) || !fp->f_op)
		return NULL;
	else
		return fp;

}

/*****************************************
 *** readFile
 *****************************************/
#ifdef _MC3XXX_SUPPORT_DOT_CALIBRATION_
static int readFile(struct file *fp, char *buf, int readlen)
{
	if (fp->f_op && fp->f_op->read)
		return fp->f_op->read(fp, buf, readlen, &fp->f_pos);
	else
		return -1;
}
#endif

/*****************************************
 *** writeFile
 *****************************************/
static int writeFile(struct file *fp, char *buf, int writelen)
{
	if (fp->f_op && fp->f_op->write)
		return fp->f_op->write(fp, buf, writelen, &fp->f_pos);
	else
		return -1;
}

/*****************************************
 *** closeFile
 *****************************************/
static int closeFile(struct file *fp)
{
	filp_close(fp, NULL);
	return 0;
}

/*****************************************
 *** initKernelEnv
 *****************************************/
static void initKernelEnv(void)
{
	oldfs = get_fs();
	set_fs(KERNEL_DS);
}

/*****************************************
 * mcube_write_log_data
 *****************************************/
static int mcube_write_log_data(struct i2c_client *client, u8 data[0x3f])
{
	#define _WRT_LOG_DATA_BUFFER_SIZE	(66 * 50)

	s16 rbm_data[3] = {0}, raw_data[3] = {0};
	int err = 0;
	char *_pszBuffer = NULL;
	int n = 0, i = 0;

	initKernelEnv();
	fd_file = openFile(DATA_PATH, O_RDWR | O_CREAT, 0);
	if (fd_file == NULL)
		ACC_LOG("%s fail to open\n", __func__);
	else {
		rbm_data[MC3XXX_AXIS_X] = (s16)((data[0x0d]) |
			(data[0x0e] << 8));
		rbm_data[MC3XXX_AXIS_Y] = (s16)((data[0x0f]) |
			(data[0x10] << 8));
		rbm_data[MC3XXX_AXIS_Z] = (s16)((data[0x11]) |
			(data[0x12] << 8));

		raw_data[MC3XXX_AXIS_X] = (rbm_data[MC3XXX_AXIS_X]
			+ offset_data[0]/2) * gsensor_gain.x / gain_data[0];
		raw_data[MC3XXX_AXIS_Y] = (rbm_data[MC3XXX_AXIS_Y]
			+ offset_data[1]/2) * gsensor_gain.y / gain_data[1];
		raw_data[MC3XXX_AXIS_Z] = (rbm_data[MC3XXX_AXIS_Z]
			+ offset_data[2]/2) * gsensor_gain.z / gain_data[2];

		_pszBuffer = kzalloc(_WRT_LOG_DATA_BUFFER_SIZE, GFP_KERNEL);

	if (_pszBuffer == NULL) {
		ACC_PR_ERR("fail to allocate memory for buffer\n");
		return -1;
	}

	memset(_pszBuffer, 0, _WRT_LOG_DATA_BUFFER_SIZE);

	n += sprintf(_pszBuffer+n, "G-sensor RAW X = %d  Y = %d  Z = %d\n",
			raw_data[0], raw_data[1], raw_data[2]);
	n += sprintf(_pszBuffer+n, "G-sensor RBM X = %d  Y = %d  Z = %d\n",
			rbm_data[0], rbm_data[1], rbm_data[2]);

	for (i = 0; i < 63; i++)
		n += sprintf(_pszBuffer+n,
		"mCube register map Register[%x] = 0x%x\n", i, data[i]);

	mdelay(50);

	err = writeFile(fd_file, _pszBuffer, n);
	if (err <= 0)
		ACC_LOG("write file error %d\n", err);

	kfree(_pszBuffer);
	set_fs(oldfs);
	closeFile(fd_file);
	}

	return 0;
}

#ifdef _MC3XXX_SUPPORT_DOT_CALIBRATION_
/*****************************************
 *** mcube_read_cali_file
 *****************************************/
static int mcube_read_cali_file(struct i2c_client *client)
{
	int cali_data[3] = {0}, cali_data1[3] = {0};
	int err = 0;
	char buf[64] = {0};

	initKernelEnv();
	fd_file = openFile(file_path, O_RDONLY, 0);

	if (fd_file == NULL) {
		ACC_LOG("%s:fail to open calibration file: %s\n",
			__func__, file_path);
		fd_file = openFile(backup_file_path, O_RDONLY, 0);

		if (fd_file == NULL) {
			ACC_LOG("%s:fail to open calibration file: %s\n",
				__func__, backup_file_path);
			cali_data[0] = 0;
			cali_data[1] = 0;
			cali_data[2] = 0;
			return -1;
		}
	} else
		memset(buf, 0, sizeof(buf));

	err = readFile(fd_file, buf, sizeof(buf));
	if (err <= 0)
		ACC_LOG("read file error %d\n", err);

	set_fs(oldfs);
	closeFile(fd_file);

	err = sscanf(buf, "%d %d %d", &cali_data[MC3XXX_AXIS_X],
			&cali_data[MC3XXX_AXIS_Y], &cali_data[MC3XXX_AXIS_Z]);

	cali_data1[MC3XXX_AXIS_X] = cali_data[MC3XXX_AXIS_X] * gsensor_gain.x
		/ GRAVITY_EARTH_1000;
	cali_data1[MC3XXX_AXIS_Y] = cali_data[MC3XXX_AXIS_Y] * gsensor_gain.y
		/ GRAVITY_EARTH_1000;
	cali_data1[MC3XXX_AXIS_Z] = cali_data[MC3XXX_AXIS_Z] * gsensor_gain.z
		/ GRAVITY_EARTH_1000;

	MC3XXX_WriteCalibration(client, cali_data1);

	return 0;
}

/*****************************************
 *** mcube_load_cali
 *****************************************/
static void	mcube_load_cali(struct i2c_client *pt_i2c_client)
{
	if (false == s_nIsCaliLoaded) {
		if (mcube_read_cali_file(pt_i2c_client)
			== MC3XXX_RETCODE_SUCCESS)
			s_nIsCaliLoaded = true;
	}
}

#endif /* _MC3XXX_SUPPORT_DOT_CALIBRATION_ */

/*****************************************
 *** mcube_psensor_ioctl
 *****************************************/
#ifdef _MC3XXX_SUPPORT_VPROXIMITY_SENSOR_
static long mcube_psensor_ioctl(struct file *file, unsigned int cmd,
	unsigned long arg)
{
	int err = 0;
	void __user *data;
	int pSensor = 0;

	if (_IOC_DIR(cmd) & _IOC_READ)
		err = !access_ok(VERIFY_WRITE, (void __user *)arg,
			_IOC_SIZE(cmd));
	else if (_IOC_DIR(cmd) & _IOC_WRITE)
		err = !access_ok(VERIFY_READ, (void __user *)arg,
			_IOC_SIZE(cmd));

	if (err) {
		PS_ERR("access error: %08X, (%2d, %2d)\n", cmd,
			_IOC_DIR(cmd), _IOC_SIZE(cmd));
		return -EFAULT;
	}

	switch (cmd) {
	case PSENSOR_IOCTL_SET_POSTURE:
		data = (void __user *)arg;
		if (data == NULL) {
			err = -EINVAL;
			break;
		}

		if (copy_from_user(&pSensor, data, sizeof(int))) {
			err = -EFAULT;
			break;
		}

		P_STATUS = pSensor;
		PS_LOG("IOCTL Get P_STATUS = %d", pSensor);
		break;
	default:

		PS_ERR("unknown IOCTL: 0x%08x\n", cmd);
		/* err = -ENOIOCTLCMD; */
		break;
	}
	return err;
}

/*****************************************
 *** STATIC STRUCTURE:: fops
 *****************************************/
static const struct file_operations mcube_psensor_fops = {
	.owner = THIS_MODULE,
	.unlocked_ioctl = mcube_psensor_ioctl,
};

/*****************************************
 *** STATIC STRUCTURE:: misc-device
 *****************************************/
static struct miscdevice mcube_psensor_device = {
	.minor = MISC_DYNAMIC_MINOR,
	.name = "psensor",
	.fops  = &mcube_psensor_fops,
};

/*****************************************
 *** psensor_ps_operate
 *****************************************/
static int psensor_ps_operate(void *self, uint32_t command, void *buff_in,
	int size_in, void *buff_out, int size_out, int *actualout)
{
	int err = 0;
	int value;
	int val =  -1;
	hwm_sensor_data *sensor_data;

	switch (command) {
	case SENSOR_DELAY:
		if ((buff_in == NULL) || (size_in < sizeof(int))) {
			PS_ERR("Set delay parameter error!\n");
			err = -EINVAL;
		}
		/* Do nothing */
		break;

	case SENSOR_ENABLE:
		value = *(int *)buff_in;
		if (value) {
			if ((buff_in == NULL) || (size_in < sizeof(int))) {
				PS_ERR("Enable sensor parameter error!\n");
				err = -EINVAL;
			} else {
				value = *(int *)buff_in;
				PS_ERR("enable ps\n");
			}
		} else
			PS_ERR("disable ps\n");

		break;

	case SENSOR_GET_DATA:
		PS_LOG("fwq get ps data !!!!!!\n");
		if ((buff_out == NULL)
			|| (size_out < sizeof(hwm_sensor_data))) {
			PS_ERR("get sensor data parameter error!\n");
			err = -EINVAL;
		} else {
			sensor_data = (hwm_sensor_data *)buff_out;
			PS_LOG("mcube sensor data P_STATUS=%d\n", P_STATUS);

		if (prev_P_STATUS != P_STATUS) {
			if (P_STATUS == 0) {
				PS_LOG("TURN ON LCD\n");
				val = 1;
				PS_LOG(
					"Set val = 1, Proximity sensor far away\n");
			} else if (P_STATUS == 1) {
				PS_LOG("TURN OFF LCD\n");
				val = 0;
				PS_LOG("Set val = 0, Proximity sensor close\n");
			}
			PS_LOG(
				"mcube sensor data prev_P_STATUS=%d, P_STATUS=%d\n",
				prev_P_STATUS, P_STATUS);
			prev_P_STATUS = P_STATUS;
			} else {
				PS_LOG("P_STATUS %5d=>%5d\n",
					prev_P_STATUS, P_STATUS);
				prev_P_STATUS = P_STATUS;
				val = (P_STATUS == 0) ? (1):(0);
		}
		sensor_data->values[0] = val;
		sensor_data->value_divide = 1;
		sensor_data->status = SENSOR_STATUS_ACCURACY_MEDIUM;
		prev_P_STATUS = P_STATUS;
		}
		break;

	default:
		PS_ERR(
			"proxmy sensor operate function has no this command %d!\n",
			command);
		err = -1;
	}

	return err;
}
#endif  /* end of _MC3XXX_SUPPORT_VPROXIMITY_SENSOR_ */

/*****************************************
 *** MC3XXX_power
 *****************************************/
static void MC3XXX_power(struct acc_hw *hw, unsigned int on)
{
}

#ifdef _MC3XXX_SUPPORT_DOT_CALIBRATION_
/*****************************************
 *** MC3XXX_rbm
 *****************************************/
static void MC3XXX_rbm(struct i2c_client *client, int enable)
{
	u8	_baDataBuf[3] = { 0 };

	_baDataBuf[0] = 0x43;
	MC3XXX_i2c_write_block(client, 0x07, _baDataBuf, 0x01);

	MC3XXX_i2c_read_block(client, 0x04, _baDataBuf, 0x01);

	if (0x00 == (_baDataBuf[0] & 0x40)) {
		_baDataBuf[0] = 0x6D;
		MC3XXX_i2c_write_block(client, 0x1B, _baDataBuf, 0x01);

		_baDataBuf[0] = 0x43;
		MC3XXX_i2c_write_block(client, 0x1B, _baDataBuf, 0x01);
	}

	if (enable == 1) {
		_baDataBuf[0] = 0x00;
		MC3XXX_i2c_write_block(client, 0x3B, _baDataBuf, 0x01);

		_baDataBuf[0] = 0x02;
		MC3XXX_i2c_write_block(client, 0x14, _baDataBuf, 0x01);

		if (s_bResolution == MC3XXX_RESOLUTION_LOW)
			gsensor_gain.x = gsensor_gain.y = gsensor_gain.z = 1024;

		s_nIsRBM_Enabled = 1;
		LPF_FirstRun = 1;
	} else if (enable == 0) {
		_baDataBuf[0] = 0x00;
		MC3XXX_i2c_write_block(client, 0x14, _baDataBuf, 0x01);

		_baDataBuf[0] = s_bPCODER;
		MC3XXX_i2c_write_block(client, 0x3B, _baDataBuf, 0x01);

		MC3XXX_SetGain();

		s_nIsRBM_Enabled = 0;
	}

	MC3XXX_i2c_read_block(client, 0x04, _baDataBuf, 0x01);

	if (_baDataBuf[0] & 0x40) {
		_baDataBuf[0] = 0x6D;
		MC3XXX_i2c_write_block(client, 0x1B, _baDataBuf, 0x01);

		_baDataBuf[0] = 0x43;
		MC3XXX_i2c_write_block(client, 0x1B, _baDataBuf, 0x01);
	}

	/* MC3XXX_i2c_read_block(client, 0x04, _baDataBuf, 0x01); */
	/* ACC_LOG("END - REG(0x04): 0x%X\n", _baDataBuf[0]); */

	_baDataBuf[0] = 0x41;
	MC3XXX_i2c_write_block(client, 0x07, _baDataBuf, 0x01);

	mdelay(220);
}

/*****************************************
 *** MC3XXX_ReadData_RBM
 *****************************************/
static int MC3XXX_ReadData_RBM(struct i2c_client *client,
	int data[MC3XXX_AXES_NUM])
{
	u8 addr = 0x0d;
	u8 rbm_buf[MC3XXX_DATA_LEN] = {0};
	int err = 0;
	int _nTemp = 0;

	if (client == NULL) {
		err = -EINVAL;
		return err;
	}

	err = MC3XXX_i2c_read_block(client, addr, rbm_buf, 0x06);
	if (err) {
		ACC_PR_ERR("error: %d\n", err);
		return err;
	}

	data[MC3XXX_AXIS_X] = (s16)((rbm_buf[0]) | (rbm_buf[1] << 8));
	data[MC3XXX_AXIS_Y] = (s16)((rbm_buf[2]) | (rbm_buf[3] << 8));
	data[MC3XXX_AXIS_Z] = (s16)((rbm_buf[4]) | (rbm_buf[5] << 8));

	if (s_bPCODE == MC3XXX_PCODE_3250) {
		_nTemp = data[MC3XXX_AXIS_X];
		data[MC3XXX_AXIS_X] = data[MC3XXX_AXIS_Y];
		data[MC3XXX_AXIS_Y] = -_nTemp;
	} else {
		if (s_bMPOL & 0x01)
			data[MC3XXX_AXIS_X] = -data[MC3XXX_AXIS_X];
		if (s_bMPOL & 0x02)
			data[MC3XXX_AXIS_Y] = -data[MC3XXX_AXIS_Y]);
	}
	return err;
}
#endif /* _MC3XXX_SUPPORT_DOT_CALIBRATION_ */

/*****************************************
 *** MC3XXX_ValidateSensorIC
 *****************************************/
static int MC3XXX_ValidateSensorIC(unsigned char *pbPCode,
	unsigned char *pbHwID)
{
	if ((*pbHwID == 0x01) || (*pbHwID == 0x03)
		|| ((*pbHwID >= 0x04) && (*pbHwID <= 0x0F))) {
		if ((*pbPCode == MC3XXX_PCODE_3210)
			|| (*pbPCode == MC3XXX_PCODE_3230)
			|| (*pbPCode == MC3XXX_PCODE_3250))
			return MC3XXX_RETCODE_SUCCESS;
	} else if ((*pbHwID == 0x02) || (*pbHwID == 0x21)
		 || ((*pbHwID >= 0x10) && (*pbHwID <= 0x1F))) {
		if ((*pbPCode == MC3XXX_PCODE_3210)
			|| (*pbPCode == MC3XXX_PCODE_3230)
			|| (*pbPCode == MC3XXX_PCODE_3250)
			|| (*pbPCode == MC3XXX_PCODE_3410)
			|| (*pbPCode == MC3XXX_PCODE_3410N)
			|| (*pbPCode == MC3XXX_PCODE_3430)
			|| (*pbPCode == MC3XXX_PCODE_3430N)) {
			return MC3XXX_RETCODE_SUCCESS;
		}
	} else if ((*pbHwID >= 0xC0) && (*pbHwID <= 0xCF)) {
		*pbPCode = (*pbPCode & 0x71);

	if ((*pbPCode == MC3XXX_PCODE_3510)
		|| (*pbPCode == MC3XXX_PCODE_3530))
		return MC3XXX_RETCODE_SUCCESS;
	} else if ((*pbHwID == 0x20) ||
		((*pbHwID >= 0x22) && (*pbHwID <= 0x2F))) {
		*pbPCode = (*pbPCode & 0xF1);

		if ((*pbPCode == MC3XXX_PCODE_3210)
			|| (*pbPCode == MC3XXX_PCODE_3216)
			|| (*pbPCode == MC3XXX_PCODE_3236)
			|| (*pbPCode == MC3XXX_PCODE_RESERVE_1)
			|| (*pbPCode == MC3XXX_PCODE_RESERVE_2)
			|| (*pbPCode == MC3XXX_PCODE_RESERVE_3)
			|| (*pbPCode == MC3XXX_PCODE_RESERVE_4)
			|| (*pbPCode == MC3XXX_PCODE_RESERVE_5)
			|| (*pbPCode == MC3XXX_PCODE_RESERVE_6)
			|| (*pbPCode == MC3XXX_PCODE_RESERVE_7)
			|| (*pbPCode == MC3XXX_PCODE_RESERVE_8)
			|| (*pbPCode == MC3XXX_PCODE_RESERVE_9))
			return MC3XXX_RETCODE_SUCCESS;
	}

	return MC3XXX_RETCODE_ERROR_IDENTIFICATION;
}

/*****************************************
 *** MC3XXX_Read_Chip_ID
 *****************************************/
static int MC3XXX_Read_Chip_ID(struct i2c_client *client, char *buf)
{
	u8	 _bChipID[4] = { 0 };

	if (!buf || !client)
		return -EINVAL;

	if (MC3XXX_i2c_read_block(client, 0x3C, _bChipID, 4)) {
		ACC_PR_ERR("[%s] i2c read fail\n", __func__);
		_bChipID[0] = 0;
		_bChipID[1] = 0;
		_bChipID[2] = 0;
		_bChipID[3] = 0;
	}

	return sprintf(buf, "%02X-%02X-%02X-%02X\n", _bChipID[3], _bChipID[2],
		_bChipID[1], _bChipID[0]);
}

/*****************************************
 *** MC3XXX_Read_Reg_Map
 *****************************************/
static int MC3XXX_Read_Reg_Map(struct i2c_client *p_i2c_client,
	u8 *pbUserBuf)
{
	u8	 _baData[MC3XXX_REGMAP_LENGTH] = { 0 };
	int	_nIndex = 0;

	if (p_i2c_client == NULL)
		return (-EINVAL);

	for (_nIndex = 0; _nIndex < MC3XXX_REGMAP_LENGTH; _nIndex++) {
		MC3XXX_i2c_read_block(p_i2c_client, _nIndex,
			&_baData[_nIndex], 1);

		if (pbUserBuf != NULL)
			pbUserBuf[_nIndex] = _baData[_nIndex];
	}

	mcube_write_log_data(p_i2c_client, _baData);

	return 0;
}

/*****************************************
 *** MC3XXX_SaveDefaultOffset
 *****************************************/
static void MC3XXX_SaveDefaultOffset(struct i2c_client *p_i2c_client)
{
	MC3XXX_i2c_read_block(p_i2c_client, 0x21, &s_baOTP_OffsetData[0], 3);
	MC3XXX_i2c_read_block(p_i2c_client, 0x24, &s_baOTP_OffsetData[3], 3);
}

/*****************************************
 *** MC3XXX_LPF
 *****************************************/
#ifdef _MC3XXX_SUPPORT_LPF_
static void MC3XXX_LPF(struct mc3xxx_i2c_data *priv, s16 data[MC3XXX_AXES_NUM])
{
	if (atomic_read(&priv->filter)) {
		if (atomic_read(&priv->fir_en)
			&& !atomic_read(&priv->suspend)) {
			int idx, firlen = atomic_read(&priv->firlen);

			if (priv->fir.num < firlen) {
				priv->fir.raw[priv->fir.num][MC3XXX_AXIS_X]
					= data[MC3XXX_AXIS_X];
				priv->fir.raw[priv->fir.num][MC3XXX_AXIS_Y]
					= data[MC3XXX_AXIS_Y];
				priv->fir.raw[priv->fir.num][MC3XXX_AXIS_Z]
					= data[MC3XXX_AXIS_Z];
				priv->fir.sum[MC3XXX_AXIS_X]
					+= data[MC3XXX_AXIS_X];
				priv->fir.sum[MC3XXX_AXIS_Y]
					+= data[MC3XXX_AXIS_Y];
				priv->fir.sum[MC3XXX_AXIS_Z]
					+= data[MC3XXX_AXIS_Z];

				priv->fir.num++;
				priv->fir.idx++;
			} else {
				idx = priv->fir.idx % firlen;
				priv->fir.sum[MC3XXX_AXIS_X]
					-= priv->fir.raw[idx][MC3XXX_AXIS_X];
				priv->fir.sum[MC3XXX_AXIS_Y]
					-= priv->fir.raw[idx][MC3XXX_AXIS_Y];
				priv->fir.sum[MC3XXX_AXIS_Z]
					-= priv->fir.raw[idx][MC3XXX_AXIS_Z];
				priv->fir.raw[idx][MC3XXX_AXIS_X]
					= data[MC3XXX_AXIS_X];
				priv->fir.raw[idx][MC3XXX_AXIS_Y]
					= data[MC3XXX_AXIS_Y];
				priv->fir.raw[idx][MC3XXX_AXIS_Z]
					= data[MC3XXX_AXIS_Z];
				priv->fir.sum[MC3XXX_AXIS_X]
					+= data[MC3XXX_AXIS_X];
				priv->fir.sum[MC3XXX_AXIS_Y]
					+= data[MC3XXX_AXIS_Y];
				priv->fir.sum[MC3XXX_AXIS_Z]
					+= data[MC3XXX_AXIS_Z];
				priv->fir.idx++;
				data[MC3XXX_AXIS_X]
					= priv->fir.sum[MC3XXX_AXIS_X] / firlen;
				data[MC3XXX_AXIS_Y]
					= priv->fir.sum[MC3XXX_AXIS_Y] / firlen;
				data[MC3XXX_AXIS_Z]
					= priv->fir.sum[MC3XXX_AXIS_Z] / firlen;
			}
		}
	}
}
#endif	/* END OF #ifdef _MC3XXX_SUPPORT_LPF_ */

#ifdef _MC3XXX_SUPPORT_LRF_
/*****************************************
 *** _MC3XXX_LowResFilter
 *****************************************/
static void _MC3XXX_LowResFilter(s16 nAxis, s16 naData[MC3XXX_AXES_NUM])
{
	#define _LRF_DIFF_COUNT_POS		2
	#define _LRF_DIFF_COUNT_NEG		(-_LRF_DIFF_COUNT_POS)
	#define _LRF_DIFF_BOUNDARY_POS	(_LRF_DIFF_COUNT_POS + 1)
	#define _LRF_DIFF_BOUNDARY_NEG	(_LRF_DIFF_COUNT_NEG - 1)
	#define _LRF_DIFF_DATA_UNCHANGE_MAX_COUNT	11

	signed int	_nCurrDiff = 0;
	signed int	_nSumDiff  = 0;
	s16		   _nCurrData = naData[nAxis];

	_nCurrDiff = (_nCurrData - s_taLRF_CB[nAxis].nRepValue);

	if ((_nCurrDiff > _LRF_DIFF_COUNT_NEG)
		&& (_nCurrDiff < _LRF_DIFF_COUNT_POS)) {
		if (s_taLRF_CB[nAxis].nIsNewRound) {
			s_taLRF_CB[nAxis].nMaxValue = _nCurrData;
			s_taLRF_CB[nAxis].nMinValue = _nCurrData;

			s_taLRF_CB[nAxis].nIsNewRound = 0;
			s_taLRF_CB[nAxis].nNewDataMonitorCount = 0;
		} else {
			if (_nCurrData > s_taLRF_CB[nAxis].nMaxValue)
				s_taLRF_CB[nAxis].nMaxValue = _nCurrData;
			else if (_nCurrData < s_taLRF_CB[nAxis].nMinValue)
				s_taLRF_CB[nAxis].nMinValue = _nCurrData;

			if (s_taLRF_CB[nAxis].nMinValue
				!= s_taLRF_CB[nAxis].nMaxValue) {
				if (_nCurrData == s_taLRF_CB[nAxis].nPreValue)
					s_taLRF_CB[nAxis]
					.nNewDataMonitorCount++;
				else
					s_taLRF_CB[nAxis]
					.nNewDataMonitorCount = 0;
			}
		}

		if (1 != (s_taLRF_CB[nAxis].nMaxValue
			- s_taLRF_CB[nAxis].nMinValue))
			s_taLRF_CB[nAxis].nRepValue
				= ((s_taLRF_CB[nAxis].nMaxValue
				+ s_taLRF_CB[nAxis].nMinValue) / 2);

		_nSumDiff = (_nCurrDiff + s_taLRF_CB[nAxis].nPreDiff);

		if (_nCurrDiff)
			s_taLRF_CB[nAxis].nPreDiff = _nCurrDiff;

		if ((_nSumDiff > _LRF_DIFF_BOUNDARY_NEG)
			&& (_nSumDiff < _LRF_DIFF_BOUNDARY_POS)) {
			if (s_taLRF_CB[nAxis].nNewDataMonitorCount
				< _LRF_DIFF_DATA_UNCHANGE_MAX_COUNT) {
				naData[nAxis] = s_taLRF_CB[nAxis].nRepValue;
				goto _LRF_RETURN;
			}
		}
	}

	s_taLRF_CB[nAxis].nRepValue   = _nCurrData;
	s_taLRF_CB[nAxis].nPreDiff	= 0;
	s_taLRF_CB[nAxis].nIsNewRound = 1;

_LRF_RETURN:

	s_taLRF_CB[nAxis].nPreValue = _nCurrData;

	#undef _LRF_DIFF_COUNT_POS
	#undef _LRF_DIFF_COUNT_NEG
	#undef _LRF_DIFF_BOUNDARY_POS
	#undef _LRF_DIFF_BOUNDARY_NEG
	#undef _LRF_DIFF_DATA_UNCHANGE_MAX_COUNT
}
#endif	/* END OF #ifdef _MC3XXX_SUPPORT_LRF_ */

/*****************************************
 *** _MC3XXX_ReadData_RBM2RAW
 *****************************************/
static void	_MC3XXX_ReadData_RBM2RAW(s16 waData[MC3XXX_AXES_NUM])
{
	waData[MC3XXX_AXIS_X] = (waData[MC3XXX_AXIS_X]
		+ offset_data[MC3XXX_AXIS_X] / 2)
		* 1024 / gain_data[MC3XXX_AXIS_X] + 8096;
	waData[MC3XXX_AXIS_Y] = (waData[MC3XXX_AXIS_Y]
		+ offset_data[MC3XXX_AXIS_Y] / 2)
		* 1024 / gain_data[MC3XXX_AXIS_Y] + 8096;
	waData[MC3XXX_AXIS_Z] = (waData[MC3XXX_AXIS_Z]
		+ offset_data[MC3XXX_AXIS_Z] / 2)
		* 1024 / gain_data[MC3XXX_AXIS_Z] + 8096;

	iAReal0_X			 = (0x0010 * waData[MC3XXX_AXIS_X]);
	iAcc1Lpf0_X		   = GetLowPassFilter(iAReal0_X, iAcc1Lpf1_X);
	iAcc0Lpf0_X		   = GetLowPassFilter(iAcc1Lpf0_X, iAcc0Lpf1_X);
	waData[MC3XXX_AXIS_X] = (iAcc0Lpf0_X / 0x0010);

	iAReal0_Y			 = (0x0010 * waData[MC3XXX_AXIS_Y]);
	iAcc1Lpf0_Y		   = GetLowPassFilter(iAReal0_Y, iAcc1Lpf1_Y);
	iAcc0Lpf0_Y		   = GetLowPassFilter(iAcc1Lpf0_Y, iAcc0Lpf1_Y);
	waData[MC3XXX_AXIS_Y] = (iAcc0Lpf0_Y / 0x0010);

	iAReal0_Z			 = (0x0010 * waData[MC3XXX_AXIS_Z]);
	iAcc1Lpf0_Z		   = GetLowPassFilter(iAReal0_Z, iAcc1Lpf1_Z);
	iAcc0Lpf0_Z		   = GetLowPassFilter(iAcc1Lpf0_Z, iAcc0Lpf1_Z);
	waData[MC3XXX_AXIS_Z] = (iAcc0Lpf0_Z / 0x0010);

	waData[MC3XXX_AXIS_X] = (waData[MC3XXX_AXIS_X] - 8096)
		* gsensor_gain.x / 1024;
	waData[MC3XXX_AXIS_Y] = (waData[MC3XXX_AXIS_Y] - 8096)
		* gsensor_gain.y / 1024;
	waData[MC3XXX_AXIS_Z] = (waData[MC3XXX_AXIS_Z] - 8096)
		* gsensor_gain.z / 1024;

	iAcc0Lpf1_X = iAcc0Lpf0_X;
	iAcc1Lpf1_X = iAcc1Lpf0_X;
	iAcc0Lpf1_Y = iAcc0Lpf0_Y;
	iAcc1Lpf1_Y = iAcc1Lpf0_Y;
	iAcc0Lpf1_Z = iAcc0Lpf0_Z;
	iAcc1Lpf1_Z = iAcc1Lpf0_Z;
}

/*****************************************
 *** MC3XXX_ReadData
 *****************************************/
static int	MC3XXX_ReadData(struct i2c_client *pt_i2c_client,
	s16 waData[MC3XXX_AXES_NUM])
{
	u8	_baData[MC3XXX_DATA_LEN] = { 0 };
	s16 _nTemp = 0;
	#ifdef _MC3XXX_SUPPORT_LPF_
		struct mc3xxx_i2c_data   *_ptPrivData = NULL;
	#endif

	if (pt_i2c_client == NULL) {
		ACC_PR_ERR("ERR: Null Pointer\n");

		return MC3XXX_RETCODE_ERROR_NULL_POINTER;
	}

	if (!s_nIsRBM_Enabled) {
		if (s_bResolution == MC3XXX_RESOLUTION_LOW) {
			if (MC3XXX_i2c_read_block(pt_i2c_client,
				MC3XXX_REG_XOUT, _baData,
				MC3XXX_LOW_REOLUTION_DATA_SIZE)) {
				ACC_PR_ERR("ERR: fail to read data via I2C!\n");

			return MC3XXX_RETCODE_ERROR_I2C;
			}

			waData[MC3XXX_AXIS_X] = ((s8) _baData[0]);
			waData[MC3XXX_AXIS_Y] = ((s8) _baData[1]);
			waData[MC3XXX_AXIS_Z] = ((s8) _baData[2]);

		#ifdef _MC3XXX_SUPPORT_LRF_
			_MC3XXX_LowResFilter(MC3XXX_AXIS_X, waData);
			_MC3XXX_LowResFilter(MC3XXX_AXIS_Y, waData);
			_MC3XXX_LowResFilter(MC3XXX_AXIS_Z, waData);
		#endif
		} else if (s_bResolution == MC3XXX_RESOLUTION_HIGH) {
			if (MC3XXX_i2c_read_block(pt_i2c_client,
				MC3XXX_REG_XOUT_EX_L, _baData,
				MC3XXX_HIGH_REOLUTION_DATA_SIZE)) {
				ACC_PR_ERR("ERR: fail to read data via I2C!\n");

			return MC3XXX_RETCODE_ERROR_I2C;
			}

			waData[MC3XXX_AXIS_X] = ((signed short) ((_baData[0])
				| (_baData[1]<<8)));
			waData[MC3XXX_AXIS_Y] = ((signed short) ((_baData[2])
				| (_baData[3]<<8)));
			waData[MC3XXX_AXIS_Z] = ((signed short) ((_baData[4])
				| (_baData[5]<<8)));
		}

	#ifdef _MC3XXX_SUPPORT_LPF_
		_ptPrivData = i2c_get_clientdata(pt_i2c_client);

		MC3XXX_LPF(_ptPrivData, waData);
	#endif
	} else {
		if (MC3XXX_i2c_read_block(pt_i2c_client, MC3XXX_REG_XOUT_EX_L,
			_baData, MC3XXX_HIGH_REOLUTION_DATA_SIZE)) {
			ACC_PR_ERR("ERR: fail to read data via I2C!\n");

		return MC3XXX_RETCODE_ERROR_I2C;
		}

		waData[MC3XXX_AXIS_X] = ((s16)((_baData[0])
			| (_baData[1] << 8)));
		waData[MC3XXX_AXIS_Y] = ((s16)((_baData[2])
			| (_baData[3] << 8)));
		waData[MC3XXX_AXIS_Z] = ((s16)((_baData[4])
			| (_baData[5] << 8)));

		_MC3XXX_ReadData_RBM2RAW(waData);
	}

	if (s_bPCODE == MC3XXX_PCODE_3250) {
		_nTemp = waData[MC3XXX_AXIS_X];
		waData[MC3XXX_AXIS_X] = waData[MC3XXX_AXIS_Y];
		waData[MC3XXX_AXIS_Y] = -_nTemp;
	} else {
		if (s_bMPOL & 0x01)
			waData[MC3XXX_AXIS_X] = -waData[MC3XXX_AXIS_X];
		if (s_bMPOL & 0x02)
			waData[MC3XXX_AXIS_Y] = -waData[MC3XXX_AXIS_Y];
	}
	return MC3XXX_RETCODE_SUCCESS;
}

/*****************************************
 *** MC3XXX_ReadOffset
 *****************************************/
static int MC3XXX_ReadOffset(struct i2c_client *client,
	s16 ofs[MC3XXX_AXES_NUM])
{
	int err = 0;
	u8 off_data[6] = {0};
	s16 _nTemp = 0;

	if (s_bResolution == MC3XXX_RESOLUTION_HIGH) {
		err = MC3XXX_i2c_read_block(client, MC3XXX_REG_XOUT_EX_L,
			off_data, MC3XXX_DATA_LEN);
		if (err) {
			ACC_PR_ERR("error: %d\n", err);
			return err;
		}
		ofs[MC3XXX_AXIS_X] = ((s16)(off_data[0]))
			| ((s16)(off_data[1])<<8);
		ofs[MC3XXX_AXIS_Y] = ((s16)(off_data[2]))
			| ((s16)(off_data[3])<<8);
		ofs[MC3XXX_AXIS_Z] = ((s16)(off_data[4]))
			| ((s16)(off_data[5])<<8);
	} else if (s_bResolution == MC3XXX_RESOLUTION_LOW) {
		err = MC3XXX_i2c_read_block(client, 0, off_data, 3);
		if (err) {
			ACC_PR_ERR("error: %d\n", err);
			return err;
		}
		ofs[MC3XXX_AXIS_X] = (s8)off_data[0];
		ofs[MC3XXX_AXIS_Y] = (s8)off_data[1];
		ofs[MC3XXX_AXIS_Z] = (s8)off_data[2];
	}

	if (s_bPCODE == MC3XXX_PCODE_3250) {
		_nTemp = ofs[0];
		ofs[0] = ofs[1];
		ofs[1] = -_nTemp;
	} else {
		if (s_bMPOL & 0x01)
			ofs[0] = -ofs[0];
		if (s_bMPOL & 0x02)
			ofs[1] = -ofs[1];
	}
	return err;
}

/*****************************************
 *** MC3XXX_ResetCalibration
 *****************************************/
static int MC3XXX_ResetCalibration(struct i2c_client *client)
{
	struct mc3xxx_i2c_data *obj = i2c_get_clientdata(client);
	u8 buf[MC3XXX_AXES_NUM] = {0x00, 0x00, 0x00};
	s16 tmp = 0;
	int err = 0;
	u8  bMsbFilter	   = 0x3F;
	s16 wSignBitMask	 = 0x2000;
	s16 wSignPaddingBits = 0xC000;

	buf[0] = 0x43;
	err = MC3XXX_i2c_write_block(client, 0x07, buf, 1);
	if (err)
		ACC_PR_ERR("error 0x07: %d\n", err);

	err = MC3XXX_i2c_write_block(client, 0x21, offset_buf, 6);
	if (err)
		ACC_PR_ERR("error: %d\n", err);

	buf[0] = 0x41;
	err = MC3XXX_i2c_write_block(client, 0x07, buf, 1);
	if (err)
		ACC_PR_ERR("error: %d\n", err);

	mdelay(20);

	if (IS_MCFM12() || IS_MCFM3X()) {
		bMsbFilter	   = 0x7F;
		wSignBitMask	 = 0x4000;
		wSignPaddingBits = 0x8000;
	}

	tmp = ((offset_buf[1] & bMsbFilter) << 8) + offset_buf[0];
	if (tmp & wSignBitMask)
		tmp |= wSignPaddingBits;
	offset_data[0] = tmp;

	tmp = ((offset_buf[3] & bMsbFilter) << 8) + offset_buf[2];
	if (tmp & wSignBitMask)
		tmp |= wSignPaddingBits;
	offset_data[1] = tmp;

	tmp = ((offset_buf[5] & bMsbFilter) << 8) + offset_buf[4];
	if (tmp & wSignBitMask)
		tmp |= wSignPaddingBits;
	offset_data[2] = tmp;

	memset(obj->cali_sw, 0x00, sizeof(obj->cali_sw));

	return err;
}

/*****************************************
 *** MC3XXX_ReadCalibration
 *****************************************/
static int MC3XXX_ReadCalibration(struct i2c_client *client,
	int dat[MC3XXX_AXES_NUM])
{
	struct mc3xxx_i2c_data *obj = i2c_get_clientdata(client);
	int err = 0;

	err = MC3XXX_ReadOffset(client, obj->offset);
	if (err) {
		ACC_PR_ERR("read offset fail, %d\n", err);
		return err;
	}

	dat[MC3XXX_AXIS_X] = obj->offset[MC3XXX_AXIS_X];
	dat[MC3XXX_AXIS_Y] = obj->offset[MC3XXX_AXIS_Y];
	dat[MC3XXX_AXIS_Z] = obj->offset[MC3XXX_AXIS_Z];

	return 0;
}

/*****************************************
 *** MC3XXX_WriteCalibration
 *****************************************/
static int MC3XXX_WriteCalibration(struct i2c_client *client,
	int dat[MC3XXX_AXES_NUM])
{
	struct mc3xxx_i2c_data *obj = i2c_get_clientdata(client);
	int err = 0;
	u8 buf[9] = { 0};
	s16 tmp = 0, x_gain = 0, y_gain = 0, z_gain = 0;
	s32 x_off = 0, y_off = 0, z_off = 0;
	int cali[MC3XXX_AXES_NUM] = {0};
	int _nTemp = 0;

	u8  bMsbFilter	   = 0x3F;
	s16 wSignBitMask	 = 0x2000;
	s16 wSignPaddingBits = 0xC000;
	s32 dwRangePosLimit  = 0x1FFF;
	s32 dwRangeNegLimit  = -0x2000;

	cali[MC3XXX_AXIS_X] = obj->cvt.sign[MC3XXX_AXIS_X]
		* (dat[obj->cvt.map[MC3XXX_AXIS_X]]);
	cali[MC3XXX_AXIS_Y] = obj->cvt.sign[MC3XXX_AXIS_Y]
		* (dat[obj->cvt.map[MC3XXX_AXIS_Y]]);
	cali[MC3XXX_AXIS_Z] = obj->cvt.sign[MC3XXX_AXIS_Z]
		* (dat[obj->cvt.map[MC3XXX_AXIS_Z]]);

	if (s_bPCODE == MC3XXX_PCODE_3250) {
		_nTemp = cali[MC3XXX_AXIS_X];
		cali[MC3XXX_AXIS_X] = -cali[MC3XXX_AXIS_Y];
		cali[MC3XXX_AXIS_Y] = _nTemp;
	} else {
		if (s_bMPOL & 0x01)
			cali[MC3XXX_AXIS_X] = -cali[MC3XXX_AXIS_X];
		if (s_bMPOL & 0x02)
			cali[MC3XXX_AXIS_Y] = -cali[MC3XXX_AXIS_Y];
	}

	/* read registers 0x21~0x29 */
	err = MC3XXX_i2c_read_block(client, 0x21, buf, 3);
	if (err) {
		ACC_PR_ERR("error: %d\n", err);
		return err;
	}
	err = MC3XXX_i2c_read_block(client, 0x24, &buf[3], 3);
	if (err) {
		ACC_PR_ERR("error: %d\n", err);
		return err;
	}
	err = MC3XXX_i2c_read_block(client, 0x27, &buf[6], 3);
	if (err) {
		ACC_PR_ERR("error: %d\n", err);
		return err;
	}

	if (IS_MCFM12() || IS_MCFM3X()) {
		bMsbFilter	   = 0x7F;
		wSignBitMask	 = 0x4000;
		wSignPaddingBits = 0x8000;
		dwRangePosLimit  = 0x3FFF;
		dwRangeNegLimit  = -0x4000;
	}

	/* get x,y,z offset */
	tmp = ((buf[1] & bMsbFilter) << 8) + buf[0];
	if (tmp & wSignBitMask)
		tmp |= wSignPaddingBits;
	x_off = tmp;

	tmp = ((buf[3] & bMsbFilter) << 8) + buf[2];
	if (tmp & wSignBitMask)
		tmp |= wSignPaddingBits;
	y_off = tmp;

	tmp = ((buf[5] & bMsbFilter) << 8) + buf[4];
	if (tmp & wSignBitMask)
		tmp |= wSignPaddingBits;
	z_off = tmp;

	/* get x,y,z gain */
	x_gain = ((buf[1] >> 7) << 8) + buf[6];
	y_gain = ((buf[3] >> 7) << 8) + buf[7];
	z_gain = ((buf[5] >> 7) << 8) + buf[8];

	/* prepare new offset */
	x_off = x_off + 16 * cali[MC3XXX_AXIS_X] * 256 * 128
		/ 3 / gsensor_gain.x / (40 + x_gain);
	y_off = y_off + 16 * cali[MC3XXX_AXIS_Y] * 256 * 128
		/ 3 / gsensor_gain.y / (40 + y_gain);
	z_off = z_off + 16 * cali[MC3XXX_AXIS_Z] * 256 * 128
		/ 3 / gsensor_gain.z / (40 + z_gain);

	/* add for over range */
	if (x_off > dwRangePosLimit)
		x_off = dwRangePosLimit;
	else if (x_off < dwRangeNegLimit)
		x_off = dwRangeNegLimit;


	if (y_off > dwRangePosLimit)
		y_off = dwRangePosLimit;
	else if (y_off < dwRangeNegLimit)
		y_off = dwRangeNegLimit;

	if (z_off > dwRangePosLimit)
		z_off = dwRangePosLimit;
	else if (z_off < dwRangeNegLimit)
		z_off = dwRangeNegLimit;

	/* storege the cerrunt offset data with DOT format */
	offset_data[0] = x_off;
	offset_data[1] = y_off;
	offset_data[2] = z_off;

	/* storege the cerrunt Gain data with GOT format */
	gain_data[0] = 256*8*128/3/(40+x_gain);
	gain_data[1] = 256*8*128/3/(40+y_gain);
	gain_data[2] = 256*8*128/3/(40+z_gain);

	buf[0] = 0x43;
	MC3XXX_i2c_write_block(client, 0x07, buf, 1);

	buf[0] = x_off & 0xff;
	buf[1] = ((x_off >> 8) & bMsbFilter) | (x_gain & 0x0100 ? 0x80 : 0);
	buf[2] = y_off & 0xff;
	buf[3] = ((y_off >> 8) & bMsbFilter) | (y_gain & 0x0100 ? 0x80 : 0);
	buf[4] = z_off & 0xff;
	buf[5] = ((z_off >> 8) & bMsbFilter) | (z_gain & 0x0100 ? 0x80 : 0);

	MC3XXX_i2c_write_block(client, 0x21, buf, 6);

	buf[0] = 0x41;
	MC3XXX_i2c_write_block(client, 0x07, buf, 1);

	mdelay(50);

	return err;
}

/*****************************************
 *** MC3XXX_SetPowerMode
 *****************************************/
static int MC3XXX_SetPowerMode(struct i2c_client *client, bool enable)
{
	u8 databuf[2] = {0};
	int res = 0;
	u8 addr = MC3XXX_REG_MODE_FEATURE;

	if (enable == mc3xxx_sensor_power)
		ACC_LOG("Sensor power status should not be set again!!!\n");

	if (MC3XXX_i2c_read_block(client, addr, databuf, 1)) {
		ACC_PR_ERR("read power ctl register err!\n");
		return MC3XXX_RETCODE_ERROR_I2C;
	}

	if (enable) {
		databuf[0] = 0x41;
		res = MC3XXX_i2c_write_block(client, MC3XXX_REG_MODE_FEATURE,
			databuf, 1);
	#ifdef _MC3XXX_SUPPORT_DOT_CALIBRATION_
		mcube_load_cali(client);
	#endif
	} else {
		databuf[0] = 0x43;
		res = MC3XXX_i2c_write_block(client, MC3XXX_REG_MODE_FEATURE,
			databuf, 1);
	}

	if (res < 0) {
		ACC_LOG("fwq set power mode failed!\n");
		return MC3XXX_RETCODE_ERROR_I2C;
	}

	mc3xxx_sensor_power = enable;

	return MC3XXX_RETCODE_SUCCESS;
}

/*****************************************
 *** MC3XXX_SetResolution
 *****************************************/
static void MC3XXX_SetResolution(void)
{
	switch (s_bPCODE) {
	case MC3XXX_PCODE_3230:
	case MC3XXX_PCODE_3430:
	case MC3XXX_PCODE_3430N:
	case MC3XXX_PCODE_3530:
	case MC3XXX_PCODE_3236:
		s_bResolution = MC3XXX_RESOLUTION_LOW;
		break;

	case MC3XXX_PCODE_3210:
	case MC3XXX_PCODE_3250:
	case MC3XXX_PCODE_3410:
	case MC3XXX_PCODE_3410N:
	case MC3XXX_PCODE_3510:
	case MC3XXX_PCODE_3216:
		s_bResolution = MC3XXX_RESOLUTION_HIGH;
		break;

	case MC3XXX_PCODE_RESERVE_10:
		ACC_PR_ERR("RESERVED ONLINE!\n");
	 /* TODO: should have a default configuration... */
		break;

	case MC3XXX_PCODE_RESERVE_1:
	case MC3XXX_PCODE_RESERVE_3:
	case MC3XXX_PCODE_RESERVE_4:
	case MC3XXX_PCODE_RESERVE_5:
	case MC3XXX_PCODE_RESERVE_6:
	case MC3XXX_PCODE_RESERVE_8:
	case MC3XXX_PCODE_RESERVE_9:
		ACC_PR_ERR("RESERVED ONLINE!\n");
		s_bResolution = MC3XXX_RESOLUTION_LOW;
		break;

	case MC3XXX_PCODE_RESERVE_2:
	case MC3XXX_PCODE_RESERVE_7:
		ACC_PR_ERR("RESERVED ONLINE!\n");
		s_bResolution = MC3XXX_RESOLUTION_HIGH;
		break;

	default:
		ACC_PR_ERR("ERR: no resolution assigned!\n");
	}
}

/*****************************************
 *** MC3XXX_SetSampleRate
 *****************************************/
static void MC3XXX_SetSampleRate(struct i2c_client *pt_i2c_client)
{
	unsigned char	_baDataBuf[2] = { 0 };

	_baDataBuf[0] = MC3XXX_REG_SAMPLE_RATE;
	_baDataBuf[1] = 0x00;

	if (IS_MCFM12() || IS_MCFM3X()) {
		unsigned char	_baData2Buf[2] = { 0 };

		_baData2Buf[0] = 0x2A;
		MC3XXX_i2c_read_block(pt_i2c_client, 0x2A, _baData2Buf, 1);

		_baData2Buf[0] = (_baData2Buf[0] & 0xC0);

		switch (_baData2Buf[0]) {
		case 0x00:
			_baDataBuf[0] = 0x00;
			break;
		case 0x40:
			_baDataBuf[0] = 0x08;
			break;
		case 0x80:
			_baDataBuf[0] = 0x09;
			break;
		case 0xC0:
			_baDataBuf[0] = 0x0A;
			break;
		default:
			ACC_PR_ERR(
				"[%s] no chance to get here... check code!\n",
				__func__);
			break;
		}
	} else
		_baDataBuf[0] = 0x00;

	MC3XXX_i2c_write_block(pt_i2c_client, MC3XXX_REG_SAMPLE_RATE,
		_baDataBuf, 1);
}

/*****************************************
 *** MC3XXX_ConfigRegRange
 *****************************************/
static void MC3XXX_ConfigRegRange(struct i2c_client *pt_i2c_client)
{
	unsigned char _baDataBuf[2] = { 0 };
	int res = 0;

	/* _baDataBuf[0] = 0x3F; */
	/*********************************************
	 * Modify low pass filter bandwidth to 512hz,
	 * for solving sensor data don't change issue
	 *********************************************/
	_baDataBuf[0] = 0x0F;

	if (s_bResolution == MC3XXX_RESOLUTION_LOW)
		_baDataBuf[0] = 0x32;

	if (IS_MCFM12() || IS_MCFM3X()) {
		if (s_bResolution == MC3XXX_RESOLUTION_LOW)
			_baDataBuf[0] = 0x02;
		else
			_baDataBuf[0] = 0x25;
	}
	res = MC3XXX_i2c_write_block(pt_i2c_client, MC3XXX_REG_RANGE_CONTROL,
		_baDataBuf, 1);
	if (res < 0)
		ACC_PR_ERR("%s fail\n", __func__);
}

/*****************************************
 *** MC3XXX_SetGain
 *****************************************/
static void MC3XXX_SetGain(void)
{
	gsensor_gain.x = gsensor_gain.y = gsensor_gain.z = 1024;

	if (s_bResolution == MC3XXX_RESOLUTION_LOW) {
		gsensor_gain.x = gsensor_gain.y = gsensor_gain.z = 86;

		if (IS_MCFM12() || IS_MCFM3X())
			gsensor_gain.x = gsensor_gain.y = gsensor_gain.z = 64;

	}
}

/*****************************************
 *** MC3XXX_Init
 *****************************************/
static int MC3XXX_Init(struct i2c_client *client, int reset_cali)
{
	unsigned char	_baDataBuf[2] = { 0 };

	#ifdef _MC3XXX_SUPPORT_POWER_SAVING_SHUTDOWN_POWER_
	if (_mc3xxx_i2c_auto_probe(client) != MC3XXX_RETCODE_SUCCESS)
		return MC3XXX_RETCODE_ERROR_I2C;
	#endif

	_baDataBuf[0] = 0x43;
	MC3XXX_i2c_write_block(client, MC3XXX_REG_MODE_FEATURE, _baDataBuf, 1);

	MC3XXX_SetResolution();
	MC3XXX_SetSampleRate(client);
	MC3XXX_ConfigRegRange(client);
	MC3XXX_SetGain();

	_baDataBuf[0] = 0x00;
	MC3XXX_i2c_write_block(client, MC3XXX_REG_TAP_DETECTION_ENABLE,
		_baDataBuf, 1);

	_baDataBuf[0] = 0x00;
	MC3XXX_i2c_write_block(client, MC3XXX_REG_INTERRUPT_ENABLE,
		_baDataBuf, 1);

	_baDataBuf[0] = 0;
	MC3XXX_i2c_read_block(client, 0x2A, _baDataBuf, 1);
	s_bMPOL = (_baDataBuf[0] & 0x03);

	#ifdef _MC3XXX_SUPPORT_DOT_CALIBRATION_
	MC3XXX_rbm(client, 0);
	#endif

	#ifdef _MC3XXX_SUPPORT_LPF_
	{
	struct mc3xxx_i2c_data   *_pt_i2c_data = i2c_get_clientdata(client);

	memset(&_pt_i2c_data->fir, 0x00, sizeof(_pt_i2c_data->fir));
	}
	#endif

	#ifdef _MC3XXX_SUPPORT_LRF_
	memset(&s_taLRF_CB, 0, sizeof(s_taLRF_CB));
	#endif

	#ifdef _MC3XXX_SUPPORT_PERIODIC_DOC_
	init_waitqueue_head(&wq_mc3xxx_open_status);
	#endif

	return MC3XXX_RETCODE_SUCCESS;
}

/*****************************************
 *** MC3XXX_ReadChipInfo
 *****************************************/
static int MC3XXX_ReadChipInfo(struct i2c_client *client, char *buf,
	int bufsize)
{
	if ((buf == NULL) || (bufsize <= 30))
		return -1;

	if (client == NULL) {
		*buf = 0;
		return -2;
	}

	sprintf(buf, "MC3XXX Chip");
	return 0;
}

/*****************************************
 *** MC3XXX_ReadSensorData
 *****************************************/
static int MC3XXX_ReadSensorData(struct i2c_client *pt_i2c_client,
	char *pbBuf, int nBufSize)
{
	int	_naAccelData[MC3XXX_AXES_NUM] = { 0 };
	struct mc3xxx_i2c_data   *_pt_i2c_obj =
		((struct mc3xxx_i2c_data *) i2c_get_clientdata(pt_i2c_client));

	if ((pt_i2c_client == NULL) || (pbBuf == NULL)) {
		ACC_PR_ERR("ERR: Null Pointer\n");
		return MC3XXX_RETCODE_ERROR_NULL_POINTER;
	}

	if (false == mc3xxx_sensor_power) {
		if (MC3XXX_SetPowerMode(pt_i2c_client, true)
			!= MC3XXX_RETCODE_SUCCESS)
			ACC_PR_ERR("ERR: fail to set power mode!\n");
	}

	#ifdef _MC3XXX_SUPPORT_DOT_CALIBRATION_
	mcube_load_cali(pt_i2c_client);

	if ((s_nIsRBM_Enabled) && (LPF_FirstRun == 1)) {
		int	_nLoopIndex = 0;

		LPF_FirstRun = 0;

		for (_nLoopIndex = 0;
			_nLoopIndex < (LPF_SamplingRate + LPF_CutoffFrequency);
			_nLoopIndex++)
			MC3XXX_ReadData(pt_i2c_client, _pt_i2c_obj->data);
	}
	#endif

	if (MC3XXX_ReadData(pt_i2c_client, _pt_i2c_obj->data)
		!= MC3XXX_RETCODE_SUCCESS) {
		ACC_PR_ERR("ERR: fail to read data!\n");

		return MC3XXX_RETCODE_ERROR_I2C;
	}

	/* output format: mg */
	_naAccelData[(_pt_i2c_obj->cvt.map[MC3XXX_AXIS_X])]
		= (_pt_i2c_obj->cvt.sign[MC3XXX_AXIS_X]
		* _pt_i2c_obj->data[MC3XXX_AXIS_X]);
	_naAccelData[(_pt_i2c_obj->cvt.map[MC3XXX_AXIS_Y])]
		= (_pt_i2c_obj->cvt.sign[MC3XXX_AXIS_Y]
		* _pt_i2c_obj->data[MC3XXX_AXIS_Y]);
	_naAccelData[(_pt_i2c_obj->cvt.map[MC3XXX_AXIS_Z])]
		= (_pt_i2c_obj->cvt.sign[MC3XXX_AXIS_Z]
		* _pt_i2c_obj->data[MC3XXX_AXIS_Z]);

	_naAccelData[MC3XXX_AXIS_X] = (_naAccelData[MC3XXX_AXIS_X]
		* GRAVITY_EARTH_1000 / gsensor_gain.x);
	_naAccelData[MC3XXX_AXIS_Y] = (_naAccelData[MC3XXX_AXIS_Y]
		* GRAVITY_EARTH_1000 / gsensor_gain.y);
	_naAccelData[MC3XXX_AXIS_Z] = (_naAccelData[MC3XXX_AXIS_Z]
		* GRAVITY_EARTH_1000 / gsensor_gain.z);

	sprintf(pbBuf, "%04x %04x %04x",
		_naAccelData[MC3XXX_AXIS_X], _naAccelData[MC3XXX_AXIS_Y],
		_naAccelData[MC3XXX_AXIS_Z]);

	return MC3XXX_RETCODE_SUCCESS;
}

/*****************************************
 *** _MC3XXX_ReadAverageData
 *****************************************/
#ifdef _MC3XXX_SUPPORT_APPLY_AVERAGE_AGORITHM_
static int _MC3XXX_ReadAverageData(struct i2c_client *client, char *buf)
{
	struct mc3xxx_i2c_data *obj =
		(struct mc3xxx_i2c_data *)i2c_get_clientdata(client);
	int acc[MC3XXX_AXES_NUM] = {0};
	s16 sensor_data[3] = {0};
	s16 sensor_data_max[3] = {0};
	s16 sensor_data_mini[3] = {0};
	s32 sensor_data_sum[3] = {0};

	int i = 0, j = 0;

	MC3XXX_ReadData(client, sensor_data);
	sensor_data_max[MC3XXX_AXIS_X] = sensor_data[MC3XXX_AXIS_X];
	sensor_data_max[MC3XXX_AXIS_Y] = sensor_data[MC3XXX_AXIS_Y];
	sensor_data_max[MC3XXX_AXIS_Z] = sensor_data[MC3XXX_AXIS_Z];

	sensor_data_mini[MC3XXX_AXIS_X] = sensor_data[MC3XXX_AXIS_X];
	sensor_data_mini[MC3XXX_AXIS_Y] = sensor_data[MC3XXX_AXIS_Y];
	sensor_data_mini[MC3XXX_AXIS_Z] = sensor_data[MC3XXX_AXIS_Z];

	sensor_data_sum[MC3XXX_AXIS_X] += sensor_data[MC3XXX_AXIS_X];
	sensor_data_sum[MC3XXX_AXIS_Y] += sensor_data[MC3XXX_AXIS_Y];
	sensor_data_sum[MC3XXX_AXIS_Z] += sensor_data[MC3XXX_AXIS_Z];

	for (i = 0; i < 11 ; i++) {
		MC3XXX_ReadData(client, sensor_data);
		sensor_data_sum[MC3XXX_AXIS_X] += sensor_data[MC3XXX_AXIS_X];
		sensor_data_sum[MC3XXX_AXIS_Y] += sensor_data[MC3XXX_AXIS_Y];
		sensor_data_sum[MC3XXX_AXIS_Z] += sensor_data[MC3XXX_AXIS_Z];
		for (j = 0; j < 3 ; j++) {
			if (sensor_data[j] > sensor_data_max[j])
				sensor_data_max[j] = sensor_data[j];

			if (sensor_data[j] < sensor_data_mini[j])
				sensor_data_mini[j] = sensor_data[j];
		}
	}
	sensor_data[MC3XXX_AXIS_X] = (s16)((sensor_data_sum[MC3XXX_AXIS_X]
		- sensor_data_max[MC3XXX_AXIS_X]
		- sensor_data_mini[MC3XXX_AXIS_X]) / 10);
	sensor_data[MC3XXX_AXIS_Y] = (s16)((sensor_data_sum[MC3XXX_AXIS_Y]
		- sensor_data_max[MC3XXX_AXIS_Y]
		- sensor_data_mini[MC3XXX_AXIS_Y]) / 10);
	sensor_data[MC3XXX_AXIS_Z] = (s16)((sensor_data_sum[MC3XXX_AXIS_Z]
		- sensor_data_max[MC3XXX_AXIS_Z]
		- sensor_data_mini[MC3XXX_AXIS_Z]) / 10);

	acc[(obj->cvt.map[MC3XXX_AXIS_X])] = obj->cvt.sign[MC3XXX_AXIS_X]
		* sensor_data[MC3XXX_AXIS_X];
	acc[(obj->cvt.map[MC3XXX_AXIS_Y])] = obj->cvt.sign[MC3XXX_AXIS_Y]
		* sensor_data[MC3XXX_AXIS_Y];
	acc[(obj->cvt.map[MC3XXX_AXIS_Z])] = obj->cvt.sign[MC3XXX_AXIS_Z]
		* sensor_data[MC3XXX_AXIS_Z];

	acc[MC3XXX_AXIS_X] = (acc[MC3XXX_AXIS_X] * GRAVITY_EARTH_1000
		/ gsensor_gain.x);
	acc[MC3XXX_AXIS_Y] = (acc[MC3XXX_AXIS_Y] * GRAVITY_EARTH_1000
		/ gsensor_gain.y);
	acc[MC3XXX_AXIS_Z] = (acc[MC3XXX_AXIS_Z] * GRAVITY_EARTH_1000
		/ gsensor_gain.z);

	sprintf(buf, "%04x %04x %04x", acc[MC3XXX_AXIS_X], acc[MC3XXX_AXIS_Y],
		acc[MC3XXX_AXIS_Z]);

	return 0;
}
#endif	/* END OF #ifdef _MC3XXX_SUPPORT_APPLY_AVERAGE_AGORITHM_ */

/*****************************************
 *** MC3XXX_ReadRawData
 *****************************************/
static int MC3XXX_ReadRawData(struct i2c_client *client, char *buf)
{
	int res = 0;

	if (!buf || !client)
		return -EINVAL;

	if (mc3xxx_sensor_power == false) {
		res = MC3XXX_SetPowerMode(client, true);
		if (res)
			ACC_PR_ERR("Power on mc3xxx error %d!\n", res);
	}

	#ifdef _MC3XXX_SUPPORT_APPLY_AVERAGE_AGORITHM_
	return _MC3XXX_ReadAverageData(client, buf);
	#else
	{
	s16 sensor_data[3] = {0};

	res = MC3XXX_ReadData(client, sensor_data);
	if (res) {
		ACC_PR_ERR("I2C error: ret value=%d", res);
		return -EIO;
	}
	sprintf(buf, "%04x %04x %04x", sensor_data[MC3XXX_AXIS_X],
	sensor_data[MC3XXX_AXIS_Y], sensor_data[MC3XXX_AXIS_Z]);
	}
	#endif

	return 0;
}

#ifdef _MC3XXX_SUPPORT_DOT_CALIBRATION_
/*****************************************
 *** MC3XXX_ReadRBMData
 *****************************************/
static int MC3XXX_ReadRBMData(struct i2c_client *client, char *buf)
{
	int res = 0;
	int data[3] = { 0 };

	if (!buf || !client)
		return -EINVAL;

	if (mc3xxx_sensor_power == false) {
		res = MC3XXX_SetPowerMode(client, true);
		if (res)
			ACC_PR_ERR("Power on mc3xxx error %d!\n", res);
	}

	res = MC3XXX_ReadData_RBM(client, data);
	if (res) {
		ACC_PR_ERR("I2C error: ret value=%d", res);
		return -EIO;
	}

	sprintf(buf, "%04x %04x %04x", data[MC3XXX_AXIS_X],
	data[MC3XXX_AXIS_Y], data[MC3XXX_AXIS_Z]);

	return res;
}
#endif /* _MC3XXX_SUPPORT_DOT_CALIBRATION_ */

/*****************************************
 *** MC3XXX_JudgeTestResult
 *****************************************/
static int MC3XXX_JudgeTestResult(struct i2c_client *client)
{
	int	res	= 0;
	int	self_result	= 0;
	s16	acc[MC3XXX_AXES_NUM] = { 0 };

	res = MC3XXX_ReadData(client, acc);
	if (res) {
		ACC_PR_ERR("I2C error: ret value=%d", res);
		return -EIO;
	}

	acc[MC3XXX_AXIS_X] = acc[MC3XXX_AXIS_X] * 1000 / gsensor_gain.x;
	acc[MC3XXX_AXIS_Y] = acc[MC3XXX_AXIS_Y] * 1000 / gsensor_gain.y;
	acc[MC3XXX_AXIS_Z] = acc[MC3XXX_AXIS_Z] * 1000 / gsensor_gain.z;

	self_result = ((acc[MC3XXX_AXIS_X] * acc[MC3XXX_AXIS_X])
		   + (acc[MC3XXX_AXIS_Y] * acc[MC3XXX_AXIS_Y])
		   + (acc[MC3XXX_AXIS_Z] * acc[MC3XXX_AXIS_Z]));

	if ((self_result > 475923) && (self_result < 2185360)) {
		ACC_PR_ERR("%s successful\n", __func__);
		return MC3XXX_RETCODE_SUCCESS;
	}

	ACC_PR_ERR("%s failt\n", __func__);
	return -EINVAL;
}

/*****************************************
 *** MC3XXX_SelfCheck
 *****************************************/
static void MC3XXX_SelfCheck(struct i2c_client *client, u8 *pUserBuf)
{
	u8	_bRData1 = 0;
	u8	_bRData2 = 0;
	u8	_bRData3 = 0;
	u8	_baDataBuf[2] = { 0 };

	MC3XXX_i2c_read_block(client, 0x20, &_bRData1, 1);
	MC3XXX_i2c_read_block(client, 0x3B, &_bRData2, 1);

	_baDataBuf[0] = 0x43;
	MC3XXX_i2c_write_block(client, 0x07, _baDataBuf, 1);

	mdelay(10);

	for ( ; ; ) {
		_baDataBuf[0] = 0x6D;
		MC3XXX_i2c_write_block(client, 0x1B, _baDataBuf, 1);

		_baDataBuf[0] = 0x43;
		MC3XXX_i2c_write_block(client, 0x1B, _baDataBuf, 1);

		_bRData3 = 0x00;
		MC3XXX_i2c_read_block(client, 0x04, &_bRData3, 1);

		if (_bRData3 & 0x40)
			break;
	}

	_baDataBuf[0] = (_bRData2 & 0xFE);
	MC3XXX_i2c_write_block(client, 0x3B, _baDataBuf, 1);

	_baDataBuf[0] = 0x03;
	MC3XXX_i2c_write_block(client, 0x20, _baDataBuf, 1);

	_baDataBuf[0] = 0x40;
	MC3XXX_i2c_write_block(client, 0x14, _baDataBuf, 1);

	mdelay(10);

	_baDataBuf[0] = pUserBuf[0];
	MC3XXX_i2c_write_block(client, 0x00, _baDataBuf, 1);

	_baDataBuf[0] = 0x41;
	MC3XXX_i2c_write_block(client, 0x07, _baDataBuf, 1);

	mdelay(10);

	_baDataBuf[0] = 0x43;
	MC3XXX_i2c_write_block(client, 0x07, _baDataBuf, 1);

	mdelay(10);

	MC3XXX_Read_Reg_Map(client, pUserBuf);

	mdelay(10);

	_baDataBuf[0] = 0x00;
	MC3XXX_i2c_write_block(client, 0x14, _baDataBuf, 1);

	_baDataBuf[0] = _bRData1;
	MC3XXX_i2c_write_block(client, 0x20, _baDataBuf, 1);

	_baDataBuf[0] = _bRData2;
	MC3XXX_i2c_write_block(client, 0x3B, _baDataBuf, 1);

	mdelay(10);

	for ( ; ; ) {
		_baDataBuf[0] = 0x6D;
		MC3XXX_i2c_write_block(client, 0x1B, _baDataBuf, 1);

		_baDataBuf[0] = 0x43;
		MC3XXX_i2c_write_block(client, 0x1B, _baDataBuf, 1);

		_bRData3 = 0xFF;
		MC3XXX_i2c_read_block(client, 0x04, &_bRData3, 1);

		if (!(_bRData3 & 0x40))
			break;
	}

	mdelay(10);
}

/*****************************************
 *** MC3XXX_GetOpenStatus
 *****************************************/
#ifdef _MC3XXX_SUPPORT_PERIODIC_DOC_
static int	MC3XXX_GetOpenStatus(void)
{
	wait_event_interruptible(wq_mc3xxx_open_status,
		(atomic_read(&s_t_mc3xxx_open_status) != 0));

	return atomic_read(&s_t_mc3xxx_open_status);
}
#endif

/*****************************************
 *** show_chipinfo_value
 *****************************************/
static ssize_t show_chipinfo_value(struct device_driver *ddri, char *buf)
{
	struct i2c_client *client = mc3xxx_i2c_client;
	char strbuf[MC3XXX_BUF_SIZE] = {0};

	if (client == NULL) {
		ACC_PR_ERR("i2c client is null!!\n");
		return 0;
	}

	MC3XXX_ReadChipInfo(client, strbuf, MC3XXX_BUF_SIZE);
	return snprintf(buf, PAGE_SIZE, "%s\n", strbuf);
}

/*****************************************
 *** show_sensordata_value
 *****************************************/
static ssize_t show_sensordata_value(struct device_driver *ddri, char *buf)
{
	struct i2c_client *client = mc3xxx_i2c_client;
	char strbuf[MC3XXX_BUF_SIZE] = { 0 };

	if (client == NULL) {
		ACC_PR_ERR("i2c client is null!!\n");
		return 0;
	}

	mc3xxx_mutex_lock();
	MC3XXX_ReadSensorData(client, strbuf, MC3XXX_BUF_SIZE);
	mc3xxx_mutex_unlock();
	return snprintf(buf, PAGE_SIZE, "%s\n", strbuf);
}

/*****************************************
 *** show_cali_value
 *****************************************/
static ssize_t show_cali_value(struct device_driver *ddri, char *buf)
{
	struct i2c_client *client = mc3xxx_i2c_client;
	struct mc3xxx_i2c_data *obj;
	int err = 0;
	int len = 0;
	int mul = 0;
	int tmp[MC3XXX_AXES_NUM] = { 0 };

	if (client == NULL) {
		ACC_PR_ERR("i2c client is null!!\n");
		return 0;
	}

	obj = (struct mc3xxx_i2c_data *) i2c_get_clientdata(client);

	err = MC3XXX_ReadOffset(client, obj->offset);
	if (err)
		return -EINVAL;

	err = MC3XXX_ReadCalibration(client, tmp);
	if (err)
		return -EINVAL;

	mul = gsensor_gain.x / mc3xxx_offset_resolution.sensitivity;
	len += snprintf(buf+len, PAGE_SIZE-len,
			"[HW] (%+3d, %+3d, %+3d) : (0x%02X, 0x%02X, 0x%02X)\n",
			obj->offset[MC3XXX_AXIS_X],
			obj->offset[MC3XXX_AXIS_Y],
			obj->offset[MC3XXX_AXIS_Z],
			obj->offset[MC3XXX_AXIS_X],
			obj->offset[MC3XXX_AXIS_Y],
			obj->offset[MC3XXX_AXIS_Z]);
	len += snprintf(buf+len, PAGE_SIZE-len,
			"[SW][%d] (%+3d, %+3d, %+3d)\n", 1,
			obj->cali_sw[MC3XXX_AXIS_X],
			obj->cali_sw[MC3XXX_AXIS_Y],
			obj->cali_sw[MC3XXX_AXIS_Z]);
	len += snprintf(buf+len, PAGE_SIZE-len,
			"[ALL]  (%+3d, %+3d, %+3d) : (%+3d, %+3d, %+3d)\n",
			obj->offset[MC3XXX_AXIS_X]*mul
			+ obj->cali_sw[MC3XXX_AXIS_X],
			obj->offset[MC3XXX_AXIS_Y]*mul
			+ obj->cali_sw[MC3XXX_AXIS_Y],
			obj->offset[MC3XXX_AXIS_Z]*mul
			+ obj->cali_sw[MC3XXX_AXIS_Z],
			tmp[MC3XXX_AXIS_X],
			tmp[MC3XXX_AXIS_Y],
			tmp[MC3XXX_AXIS_Z]);

		return len;
}

/*****************************************
 *** store_cali_value
 *****************************************/
static ssize_t store_cali_value(struct device_driver *ddri, const char *buf,
	size_t count)
{
	struct i2c_client *client = mc3xxx_i2c_client;
	int err = 0;
	int x = 0;
	int y = 0;
	int z = 0;
	int dat[MC3XXX_AXES_NUM] = {0};

	if (!strncmp(buf, "rst", 3)) {
		mc3xxx_mutex_lock();
		err = MC3XXX_ResetCalibration(client);
		mc3xxx_mutex_unlock();

		if (err)
			ACC_PR_ERR("reset offset err = %d\n", err);
	} else if (sscanf(buf, "0x%02X 0x%02X 0x%02X", &x, &y, &z) == 3) {
		dat[MC3XXX_AXIS_X] = x;
		dat[MC3XXX_AXIS_Y] = y;
		dat[MC3XXX_AXIS_Z] = z;

		mc3xxx_mutex_lock();
		err = MC3XXX_WriteCalibration(client, dat);
		mc3xxx_mutex_unlock();
		if (err)
			ACC_PR_ERR("write calibration err = %d\n", err);
	} else
		ACC_PR_ERR("invalid format\n");

	return count;
}

/*****************************************
 *** show_selftest_value
 *****************************************/
static ssize_t show_selftest_value(struct device_driver *ddri, char *buf)
{
	struct i2c_client *client = mc3xxx_i2c_client;

	if (client == NULL) {
		ACC_PR_ERR("i2c client is null!!\n");
		return 0;
	}

	return snprintf(buf, 8, "%s\n", selftestRes);
}

/*****************************************
 *** store_selftest_value
 *****************************************/
static ssize_t store_selftest_value(struct device_driver *ddri,
	const char *buf, size_t count)
{   /*write anything to this register will trigger the process*/
	struct i2c_client *client = mc3xxx_i2c_client;
	int num = 0;
	int ret = 0;

	ret = kstrtoint(buf, 10, &num);
	if (ret != 0) {
		ACC_PR_ERR("parse number fail\n");
		return count;
	} else if (num == 0) {
		ACC_PR_ERR("invalid data count\n");
		return count;
	}

	mc3xxx_mutex_lock();
	MC3XXX_SetPowerMode(client, true);
	mc3xxx_mutex_unlock();

	if (!MC3XXX_JudgeTestResult(client)) {
		strcpy(selftestRes, "y");
	} else {
		ACC_LOG("SELFTEST : FAIL\n");
		strcpy(selftestRes, "n");
	}

	return count;
}

/*****************************************
 *** show_firlen_value
 *****************************************/
static ssize_t show_firlen_value(struct device_driver *ddri, char *buf)
{
	#ifdef _MC3XXX_SUPPORT_LPF_
	struct i2c_client *client = mc3xxx_i2c_client;
	struct mc3xxx_i2c_data *obj = i2c_get_clientdata(client);

	return snprintf(buf, PAGE_SIZE, "%d\n", atomic_read(&obj->firlen));
	#else
	return snprintf(buf, PAGE_SIZE, "not support\n");
	#endif
}

/*****************************************
 *** store_firlen_value
 *****************************************/
static ssize_t store_firlen_value(struct device_driver *ddri,
	const char *buf, size_t count)
{
	#ifdef _MC3XXX_SUPPORT_LPF_
	struct i2c_client *client = mc3xxx_i2c_client;
	struct mc3xxx_i2c_data *obj = i2c_get_clientdata(client);
	int firlen = 0;
	int ret = 0;

	ret = kstrtoint(buf, 10, &firlen);
	if (ret != 0)
		ACC_PR_ERR("invallid format\n");
	else if (firlen > C_MAX_FIR_LENGTH)
		ACC_PR_ERR("exceeds maximum filter length\n");
	else {
		atomic_set(&obj->firlen, firlen);
		if (firlen == 0)
			atomic_set(&obj->fir_en, 0);
		else {
			memset(&obj->fir, 0x00, sizeof(obj->fir));
			atomic_set(&obj->fir_en, 1);
		}
	}
	#endif
	return count;
}

/*****************************************
 *** show_trace_value
 *****************************************/
static ssize_t show_trace_value(struct device_driver *ddri, char *buf)
{
	ssize_t res = 0;
	struct mc3xxx_i2c_data *obj = mc3xxx_obj_i2c_data;

	if (obj == NULL) {
		ACC_PR_ERR("i2c_data obj is null!!\n");
		return 0;
	}

	res = snprintf(buf, PAGE_SIZE, "0x%04X\n", atomic_read(&obj->trace));
	return res;
}

/*****************************************
 *** store_trace_value
 *****************************************/
static ssize_t store_trace_value(struct device_driver *ddri,
	const char *buf, size_t count)
{
	struct mc3xxx_i2c_data *obj = mc3xxx_obj_i2c_data;
	int trace = 0;

	if (obj == NULL) {
		ACC_PR_ERR("i2c_data obj is null!!\n");
		return 0;
	}

	if (sscanf(buf, "0x%x", &trace) == 1)
		atomic_set(&obj->trace, trace);
	else
		ACC_PR_ERR("invalid content: '%s', length = %zu\n", buf, count);

	return count;
}

/*****************************************
 *** show_status_value
 *****************************************/
static ssize_t show_status_value(struct device_driver *ddri, char *buf)
{
	ssize_t len = 0;
	struct mc3xxx_i2c_data *obj = mc3xxx_obj_i2c_data;

	if (obj == NULL) {
		ACC_PR_ERR("i2c_data obj is null!!\n");
		return 0;
	}

	if (obj->hw)
		len += snprintf(buf+len, PAGE_SIZE-len, "CUST: %d %d (%d %d)\n",
			obj->hw->i2c_num, obj->hw->direction, obj->hw->power_id,
			obj->hw->power_vol);
	else
		len += snprintf(buf+len, PAGE_SIZE-len, "CUST: NULL\n");

	return len;
}

/*****************************************
 *** show_power_status
 *****************************************/
static ssize_t show_power_status(struct device_driver *ddri, char *buf)
{
	ssize_t res = 0;
	u8 uData = 0;
	struct mc3xxx_i2c_data *obj = mc3xxx_obj_i2c_data;

	if (obj == NULL) {
		ACC_PR_ERR("i2c_data obj is null!!\n");
		return 0;
	}
	MC3XXX_i2c_read_block(obj->client, MC3XXX_REG_MODE_FEATURE, &uData, 1);

	res = snprintf(buf, PAGE_SIZE, "0x%04X\n", uData);
	return res;
}

/*****************************************
 *** show_version_value
 *****************************************/
static ssize_t show_version_value(struct device_driver *ddri, char *buf)
{
	if (1 == VIRTUAL_Z)
		return snprintf(buf, PAGE_SIZE, "%s\n",
			MC3XXX_DEV_DRIVER_VERSION_VIRTUAL_Z);
	else
		return snprintf(buf, PAGE_SIZE, "%s\n",
			MC3XXX_DEV_DRIVER_VERSION);
}

/*****************************************
 *** show_chip_id
 *****************************************/
static ssize_t show_chip_id(struct device_driver *ddri, char *buf)
{
	struct mc3xxx_i2c_data   *_pt_i2c_data = mc3xxx_obj_i2c_data;

	return MC3XXX_Read_Chip_ID(_pt_i2c_data->client, buf);
}

/*****************************************
 *** show_virtual_z
 *****************************************/
static ssize_t show_virtual_z(struct device_driver *ddri, char *buf)
{
	return snprintf(buf, PAGE_SIZE, "%s\n",
		VIRTUAL_Z == 1 ? "Virtual Z Support" : "No Virtual Z Support");
}

/*****************************************
 *** show_regiter_map
 *****************************************/
static ssize_t show_regiter_map(struct device_driver *ddri, char *buf)
{
	u8		 _bIndex	   = 0;
	u8		 _baRegMap[64] = { 0 };
	ssize_t	_tLength	  = 0;

	struct i2c_client *client = mc3xxx_i2c_client;

	if ((buf[0] == 0xA5) && (buf[1] == 0x7B) && (buf[2] == 0x40)) {
		mc3xxx_mutex_lock();
		MC3XXX_Read_Reg_Map(client, buf);
		mc3xxx_mutex_unlock();

		buf[0x21] = s_baOTP_OffsetData[0];
		buf[0x22] = s_baOTP_OffsetData[1];
		buf[0x23] = s_baOTP_OffsetData[2];
		buf[0x24] = s_baOTP_OffsetData[3];
		buf[0x25] = s_baOTP_OffsetData[4];
		buf[0x26] = s_baOTP_OffsetData[5];

		_tLength = 64;
	} else {
		mc3xxx_mutex_lock();
		MC3XXX_Read_Reg_Map(client, _baRegMap);
		mc3xxx_mutex_unlock();

	for (_bIndex = 0; _bIndex < 64; _bIndex++)
		_tLength += snprintf((buf + _tLength), (PAGE_SIZE - _tLength),
		"Reg[0x%02X]: 0x%02X\n", _bIndex, _baRegMap[_bIndex]);
	}

	return _tLength;
}

/*****************************************
 *** store_regiter_map
 *****************************************/
static ssize_t store_regiter_map(struct device_driver *ddri,
	const char *buf, size_t count)
{
	return count;
}

/*****************************************
 *** show_chip_orientation
 *****************************************/
static ssize_t show_chip_orientation(struct device_driver *ptDevDrv,
	char *pbBuf)
{
	ssize_t		  _tLength = 0;
	struct acc_hw   *_ptAccelHw = get_cust_acc();

	_tLength = snprintf(pbBuf, PAGE_SIZE, "default direction = %d\n",
		_ptAccelHw->direction);

	return _tLength;
}

/*****************************************
 *** store_chip_orientation
 *****************************************/
static ssize_t store_chip_orientation(struct device_driver *ptDevDrv,
	const char *pbBuf, size_t tCount)
{
	int _nDirection = 0;
	int ret = 0;
	struct mc3xxx_i2c_data   *_pt_i2c_obj = mc3xxx_obj_i2c_data;

	if (_pt_i2c_obj == NULL)
		return 0;

	ret = kstrtoint(pbBuf, 10, &_nDirection);
	if (ret == 0) {
		if (hwmsen_get_convert(_nDirection, &_pt_i2c_obj->cvt))
			ACC_PR_ERR("ERR: fail to set direction\n");
	}

	return tCount;
}

/*****************************************
 *** show_accuracy_status
 *****************************************/
static ssize_t show_accuracy_status(struct device_driver *ddri, char *buf)
{
	return snprintf(buf, PAGE_SIZE, "%d\n", s_bAccuracyStatus);
}

/*****************************************
 *** store_accuracy_status
 *****************************************/
static ssize_t store_accuracy_status(struct device_driver *ddri,
	const char *buf, size_t count)
{
	int	_nAccuracyStatus = 0;
	int ret = 0;

	ret = kstrtoint(buf, 10, &_nAccuracyStatus);
	if (ret != 0) {
		ACC_PR_ERR("incorrect argument\n");
		return count;
	}

	if (_nAccuracyStatus > SENSOR_STATUS_ACCURACY_HIGH) {
		ACC_PR_ERR("illegal accuracy status\n");
		return count;
	}

	s_bAccuracyStatus = ((int8_t) _nAccuracyStatus);

	return count;
}

/*****************************************
 *** show_selfcheck_value
 *****************************************/
static ssize_t show_selfcheck_value(struct device_driver *ptDevDriver,
	char *pbBuf)
{
	struct i2c_client   *_pt_i2c_client = mc3xxx_i2c_client;

	/* ACC_LOG("[%s] 0x%02X\n", __FUNCTION__, pbBuf[0]); */

	mc3xxx_mutex_lock();
	MC3XXX_SelfCheck(_pt_i2c_client, pbBuf);
	MC3XXX_Init(_pt_i2c_client, 0);
	mc3xxx_mutex_unlock();

	return 64;
}

/*****************************************
 *** store_selfcheck_value
 *****************************************/
static ssize_t store_selfcheck_value(struct device_driver *ddri,
	const char *buf, size_t count)
{
	/* reserved */
	/* ACC_LOG("[%s] buf[0]: 0x%02X\n", __FUNCTION__, buf[0]); */

	return count;
}

/*****************************************
 *** show_chip_validate_value
 *****************************************/
static ssize_t show_chip_validate_value(struct device_driver *ptDevDriver,
	char *pbBuf)
{
	unsigned char	_bChipValidation = 0;

	_bChipValidation = MC3XXX_ValidateSensorIC(&s_bPCODE, &s_bHWID);

	return snprintf(pbBuf, PAGE_SIZE, "%d\n", _bChipValidation);
}

/*****************************************
 *** show_pdoc_enable_value
 *****************************************/
static ssize_t show_pdoc_enable_value(struct device_driver *ptDevDriver,
	char *pbBuf)
{
	#ifdef _MC3XXX_SUPPORT_PERIODIC_DOC_
	return snprintf(pbBuf, PAGE_SIZE, "%d\n", s_bIsPDOC_Enabled);
	#else
	unsigned char	_bIsPDOC_Enabled = false;

	return snprintf(pbBuf, PAGE_SIZE, "%d\n", _bIsPDOC_Enabled);
	#endif
}

/*****************************************
 *** DRIVER ATTRIBUTE LIST TABLE
 *****************************************/
static DRIVER_ATTR(chipinfo,	0444, show_chipinfo_value,   NULL);
static DRIVER_ATTR(sensordata,	0444, show_sensordata_value, NULL);
static DRIVER_ATTR(cali, 0644, show_cali_value,	   store_cali_value);
static DRIVER_ATTR(selftest, 0644, show_selftest_value,   store_selftest_value);
static DRIVER_ATTR(firlen, 0644, show_firlen_value,	 store_firlen_value);
static DRIVER_ATTR(trace, 0644, show_trace_value,	  store_trace_value);
static DRIVER_ATTR(status, 0444, show_status_value,	 NULL);
static DRIVER_ATTR(power,	0444, show_power_status,	 NULL);
static DRIVER_ATTR(version, 0444, show_version_value,	NULL);
static DRIVER_ATTR(chipid,	 0444, show_chip_id,		  NULL);
static DRIVER_ATTR(virtualz, 0444, show_virtual_z,		NULL);
static DRIVER_ATTR(regmap, 0644, show_regiter_map,	  store_regiter_map);
static DRIVER_ATTR(orientation, 0644, show_chip_orientation,
	store_chip_orientation);
static DRIVER_ATTR(accuracy, 0644, show_accuracy_status, store_accuracy_status);
static DRIVER_ATTR(selfcheck, 0644, show_selfcheck_value,
	store_selfcheck_value);
static DRIVER_ATTR(validate, 0444, show_chip_validate_value, NULL);
static DRIVER_ATTR(pdoc, 0444, show_pdoc_enable_value, NULL);

static struct driver_attribute *mc3xxx_attr_list[] = {
	&driver_attr_chipinfo,
	&driver_attr_sensordata,
	&driver_attr_cali,
	&driver_attr_selftest,
	&driver_attr_firlen,
	&driver_attr_trace,
	&driver_attr_status,
	&driver_attr_power,
	&driver_attr_version,
	&driver_attr_chipid,
	&driver_attr_virtualz,
	&driver_attr_regmap,
	&driver_attr_orientation,
	&driver_attr_accuracy,
	&driver_attr_selfcheck,
	&driver_attr_validate,
	&driver_attr_pdoc,
};

/*****************************************
 *** mc3xxx_create_attr
 *****************************************/
static int mc3xxx_create_attr(struct device_driver *driver)
{
	int idx, err = 0;
	int num = (int)(ARRAY_SIZE(mc3xxx_attr_list));

	if (driver == NULL)
		return -EINVAL;

	for (idx = 0; idx < num; idx++) {
		err = driver_create_file(driver, mc3xxx_attr_list[idx]);
		if (err) {
			ACC_PR_ERR("driver_create_file (%s) = %d\n",
				mc3xxx_attr_list[idx]->attr.name, err);
			break;
		}
	}
	return err;
}

/*****************************************
 *** mc3xxx_delete_attr
 *****************************************/
static int mc3xxx_delete_attr(struct device_driver *driver)
{
	int idx, err = 0;
	int num = (int)(ARRAY_SIZE(mc3xxx_attr_list));

	if (driver == NULL)
		return -EINVAL;

	for (idx = 0; idx < num; idx++)
		driver_remove_file(driver, mc3xxx_attr_list[idx]);

	return err;
}

/*****************************************
 *** MC3XXX_reset
 *****************************************/
static void MC3XXX_reset(struct i2c_client *client)
{
	unsigned char	_baBuf[2] = { 0 };

	_baBuf[0] = 0x43;

	MC3XXX_i2c_write_block(client, MC3XXX_REG_MODE_FEATURE, _baBuf, 0x01);

	MC3XXX_i2c_read_block(client, 0x04, _baBuf, 0x01);

	if (0x00 == (_baBuf[0] & 0x40)) {
		_baBuf[0] = 0x6D;
		MC3XXX_i2c_write_block(client, 0x1B, _baBuf, 0x01);

		_baBuf[0] = 0x43;
		MC3XXX_i2c_write_block(client, 0x1B, _baBuf, 0x01);
	}

	_baBuf[0] = 0x43;
	MC3XXX_i2c_write_block(client, 0x07, _baBuf, 1);

	_baBuf[0] = 0x80;
	MC3XXX_i2c_write_block(client, 0x1C, _baBuf, 1);

	_baBuf[0] = 0x80;
	MC3XXX_i2c_write_block(client, 0x17, _baBuf, 1);

	mdelay(5);

	_baBuf[0] = 0x00;
	MC3XXX_i2c_write_block(client, 0x1C, _baBuf, 1);

	_baBuf[0] = 0x00;
	MC3XXX_i2c_write_block(client, 0x17, _baBuf, 1);

	mdelay(5);

	MC3XXX_i2c_read_block(client, 0x21, offset_buf, 6);

	MC3XXX_i2c_read_block(client, 0x04, _baBuf, 0x01);

	if (_baBuf[0] & 0x40) {
		_baBuf[0] = 0x6D;
		MC3XXX_i2c_write_block(client, 0x1B, _baBuf, 0x01);

		_baBuf[0] = 0x43;
		MC3XXX_i2c_write_block(client, 0x1B, _baBuf, 0x01);
	}

	_baBuf[0] = 0x41;

	MC3XXX_i2c_write_block(client, MC3XXX_REG_MODE_FEATURE, _baBuf, 0x01);
}

/*****************************************
 *** mc3xxx_suspend
 *****************************************/
static int mc3xxx_suspend(struct device *dev)
{
	struct i2c_client *client = to_i2c_client(dev);
	struct mc3xxx_i2c_data *obj = i2c_get_clientdata(client);
	int err = 0;

	if (obj == NULL) {
		ACC_PR_ERR("null pointer!!\n");
		return -EINVAL;
	}

	atomic_set(&obj->suspend, 1);
	mc3xxx_mutex_lock();
	err = MC3XXX_SetPowerMode(client, false);
	mc3xxx_mutex_unlock();
	if (err) {
		ACC_PR_ERR("write power control fail!!\n");
		return err;
	}
	MC3XXX_power(obj->hw, 0);

	return err;
}

/*****************************************
 *** mc3xxx_resume
 *****************************************/
static int mc3xxx_resume(struct device *dev)
{
	struct i2c_client *client = to_i2c_client(dev);
	struct mc3xxx_i2c_data *obj = i2c_get_clientdata(client);
	int err;

	if (obj == NULL) {
		ACC_PR_ERR("null pointer!!\n");
		return -EINVAL;
	}

	MC3XXX_power(obj->hw, 1);
	mc3xxx_mutex_lock();
	err = MC3XXX_Init(client, 0);
	if (err) {
		mc3xxx_mutex_unlock();
		ACC_PR_ERR("initialize client fail!!\n");
		return err;
	}

	err = MC3XXX_SetPowerMode(client, true);
	mc3xxx_mutex_unlock();
	if (err) {
		ACC_PR_ERR("write power control fail!!\n");
		return err;
	}
	atomic_set(&obj->suspend, 0);
	return 0;
}


/*****************************************
 *** _mc3xxx_i2c_auto_probe
 *****************************************/
static int _mc3xxx_i2c_auto_probe(struct i2c_client *client)
{
	#define _MC3XXX_I2C_PROBE_ADDR_COUNT_	\
		(ARRAY_SIZE(mc3xxx_i2c_auto_probe_addr))
	unsigned char	_baData1Buf[2] = { 0 };
	unsigned char	_baData2Buf[2] = { 0 };
	int _nCount = 0;
	int _naCheckCount[_MC3XXX_I2C_PROBE_ADDR_COUNT_] = { 0 };

	memset(_naCheckCount, 0, sizeof(_naCheckCount));
_I2C_AUTO_PROBE_RECHECK_:
	s_bPCODE  = 0x00;
	s_bPCODER = 0x00;
	s_bHWID   = 0x00;

	for (_nCount = 0; _nCount < _MC3XXX_I2C_PROBE_ADDR_COUNT_; _nCount++) {
		client->addr = mc3xxx_i2c_auto_probe_addr[_nCount];
		_baData1Buf[0] = 0;
		if (MC3XXX_i2c_read_block(client, 0x3B, _baData1Buf, 1) < 0)
			continue;
		_naCheckCount[_nCount]++;

		if (_baData1Buf[0] == 0x00) {
			if (_naCheckCount[_nCount] == 1) {
				MC3XXX_reset(client);
				mdelay(3);
				goto _I2C_AUTO_PROBE_RECHECK_;
			} else
				continue;
		}

		_baData2Buf[0] = 0;
		MC3XXX_i2c_read_block(client, 0x18, _baData2Buf, 1);
		s_bPCODER = _baData1Buf[0];
		if (MC3XXX_ValidateSensorIC(&_baData1Buf[0], &_baData2Buf[0])
			== MC3XXX_RETCODE_SUCCESS) {
			s_bPCODE = _baData1Buf[0];
			s_bHWID  = _baData2Buf[0];
			MC3XXX_SaveDefaultOffset(client);

			return MC3XXX_RETCODE_SUCCESS;
		}
	}

	return MC3XXX_RETCODE_ERROR_I2C;
	#undef _MC3XXX_I2C_PROBE_ADDR_COUNT_
}

/**************************************************************
 * if use  this type of enable ,
 * Gsensor should report inputEvent(x, y, z ,stats, div) to HAL
 **************************************************************/
static int mc3xxx_open_report_data(int open)
{
	/* should queuq work to report event if  is_report_input_direct=true */
	return 0;
}

/**************************************************************
 * if use  this type of enable ,
 * Gsensor only enabled but not report inputEvent to HAL
 *************************************************************/
static int mc3xxx_enable_nodata(int en)
{
	int res = 0;
	int retry = 0;
	bool power = false;

	if (en == 1)
		power = true;
	if (en == 0)
		power = false;

	for (retry = 0; retry < 3; retry++) {
		res = MC3XXX_SetPowerMode(mc3xxx_obj_i2c_data->client, power);
		if (res == 0)
			break;
		ACC_LOG("MC3XXX_SetPowerMode fail\n");
	}

	if (res != 0) {
		ACC_LOG("MC3XXX_SetPowerMode fail!\n");
		return -1;
	}
	return 0;
}

static int mc3xxx_set_delay(u64 ns)
{
	int value = 0;

	value = (int)ns/1000/1000;
	return 0;
}

static int mc3xxx_get_data(int *x, int *y, int *z, int *status)
{
	char buff[MC3XXX_BUF_SIZE];
	int ret;

	MC3XXX_ReadSensorData(mc3xxx_obj_i2c_data->client, buff,
		MC3XXX_BUF_SIZE);
	ret = sscanf(buff, "%x %x %x", x, y, z);
	*status = SENSOR_STATUS_ACCURACY_MEDIUM;

	return 0;
}


static int mc3xxx_batch(int flag, int64_t samplingPeriodNs,
	int64_t maxBatchReportLatencyNs)
{
	int value = 0;
/*
 *	int err = 0;
 *	int sample_delay = 0;
 */

	value = (int)samplingPeriodNs/1000/1000;
	/*FIX ME */
	/*NEED SUPPORT batch ?*/
/*
 *	if (value <= 5)
 *		sample_delay = MC3410_ACCEL_ODR_400HZ;
 *	else if (value <= 10)
 *		sample_delay = MC3410_ACCEL_ODR_200HZ;
 *	else
 *		sample_delay = MC3410_ACCEL_ODR_100HZ;
 *
 *	err = MC3XXX_SetSampleRate(mc3xxx_obj_i2c_data->client);
 *
 *	if (err < 0) {
 *		ACC_PR_ERR("set delay parameter error!\n");
 *		return -1;
 *	}
 *	ACC_LOG("mc3xxx_batch acc set delay = (%d) ok.\n", value);
 */
	return 0;
}

static int mc3xxx_flush(void)
{
	return acc_flush_report();
}

static int mc3xxx_factory_enable_sensor(bool enabledisable,
	int64_t sample_periods_ms)
{
	int err;

	err = mc3xxx_enable_nodata(enabledisable == true ? 1 : 0);
	if (err) {
		ACC_PR_ERR("%s enable sensor failed!\n", __func__);
		return -1;
	}
	err = mc3xxx_batch(0, sample_periods_ms * 1000000, 0);
	if (err) {
		ACC_PR_ERR("%s enable set batch failed!\n", __func__);
		return -1;
	}
	return 0;
}
static int mc3xxx_factory_get_data(int32_t data[3], int *status)
{
	return mc3xxx_get_data(&data[0], &data[1], &data[2], status);

}
static int mc3xxx_factory_get_raw_data(int32_t data[3])
{
	char strbuf[MC3XXX_BUF_SIZE] = { 0 };

	MC3XXX_ReadRawData(mc3xxx_i2c_client, strbuf);
	return 0;
}
static int mc3xxx_factory_enable_calibration(void)
{
	return 0;
}
static int mc3xxx_factory_clear_cali(void)
{
	int err = 0;

	err = MC3XXX_ResetCalibration(mc3xxx_i2c_client);
	if (err) {
		ACC_PR_ERR("mc3xxx_ResetCalibration failed!\n");
		return -1;
	}
	return 0;
}
static int mc3xxx_factory_set_cali(int32_t data[3])
{
	int err = 0;
	int cali[3] = { 0 };

	/* obj */
	mc3xxx_obj_i2c_data->cali_sw[MC3XXX_AXIS_X] += data[0];
	mc3xxx_obj_i2c_data->cali_sw[MC3XXX_AXIS_Y] += data[1];
	mc3xxx_obj_i2c_data->cali_sw[MC3XXX_AXIS_Z] += data[2];

	cali[MC3XXX_AXIS_X] = data[0] * gsensor_gain.x / GRAVITY_EARTH_1000;
	cali[MC3XXX_AXIS_Y] = data[1] * gsensor_gain.y / GRAVITY_EARTH_1000;
	cali[MC3XXX_AXIS_Z] = data[2] * gsensor_gain.z / GRAVITY_EARTH_1000;
	err = MC3XXX_WriteCalibration(mc3xxx_i2c_client, cali);
	if (err) {
		ACC_PR_ERR("mc3xxx_WriteCalibration failed!\n");
		return -1;
	}
	return 0;
}
static int mc3xxx_factory_get_cali(int32_t data[3])
{
	data[0] = mc3xxx_obj_i2c_data->cali_sw[MC3XXX_AXIS_X];
	data[1] = mc3xxx_obj_i2c_data->cali_sw[MC3XXX_AXIS_Y];
	data[2] = mc3xxx_obj_i2c_data->cali_sw[MC3XXX_AXIS_Z];
	return 0;
}
static int mc3xxx_factory_do_self_test(void)
{
	return 0;
}

static struct accel_factory_fops mc3xxx_factory_fops = {
	.enable_sensor = mc3xxx_factory_enable_sensor,
	.get_data = mc3xxx_factory_get_data,
	.get_raw_data = mc3xxx_factory_get_raw_data,
	.enable_calibration = mc3xxx_factory_enable_calibration,
	.clear_cali = mc3xxx_factory_clear_cali,
	.set_cali = mc3xxx_factory_set_cali,
	.get_cali = mc3xxx_factory_get_cali,
	.do_self_test = mc3xxx_factory_do_self_test,
};

static struct accel_factory_public mc3xxx_factory_device = {
	.gain = 1,
	.sensitivity = 1,
	.fops = &mc3xxx_factory_fops,
};


/*****************************************
 *** mc3xxx_i2c_probe
 *****************************************/
static int mc3xxx_i2c_probe(struct i2c_client *client,
	const struct i2c_device_id *id)
{
	struct i2c_client *new_client;
	struct mc3xxx_i2c_data *obj;
	struct acc_control_path ctl = {0};
	struct acc_data_path data = {0};
	int err = 0;

	err = get_accel_dts_func(client->dev.of_node, hw);
	if (err < 0) {
		ACC_PR_ERR("get cust_baro dts info fail\n");
		goto exit;
	}

	if (_mc3xxx_i2c_auto_probe(client) != MC3XXX_RETCODE_SUCCESS)
		goto exit;

	obj = kzalloc(sizeof(*obj), GFP_KERNEL);
	if (!obj) {
		err = -ENOMEM;
		goto exit;
	}

	obj->hw = hw;
	err = hwmsen_get_convert(obj->hw->direction, &obj->cvt);
	if (err) {
		ACC_PR_ERR("invalid direction: %d\n", obj->hw->direction);
		goto exit_kfree;
	}

	mc3xxx_obj_i2c_data = obj;
	obj->client = client;
	new_client = obj->client;
	i2c_set_clientdata(new_client, obj);
	atomic_set(&obj->trace, 0);
	atomic_set(&obj->suspend, 0);

	#ifdef _MC3XXX_SUPPORT_LPF_
	if (obj->hw->firlen > C_MAX_FIR_LENGTH)
		atomic_set(&obj->firlen, C_MAX_FIR_LENGTH);
	else
		atomic_set(&obj->firlen, obj->hw->firlen);

	if (atomic_read(&obj->firlen) > 0)
		atomic_set(&obj->fir_en, 1);
	#endif
	mc3xxx_i2c_client = new_client;
	MC3XXX_reset(new_client);

	if (_mc3xxx_i2c_auto_probe(client) != MC3XXX_RETCODE_SUCCESS)
		goto exit_init_failed;

	MC3XXX_i2c_read_block(client, 0x21, offset_buf, 6);
	err = MC3XXX_Init(new_client, 1);
	if (err)
		goto exit_init_failed;
	mc3xxx_mutex_init();
	ctl.is_use_common_factory = false;
	/* factory */
	err = accel_factory_device_register(&mc3xxx_factory_device);
	if (err) {
		ACC_PR_ERR("acc_factory register failed.\n");
		goto exit_misc_device_register_failed;
	}

	err = mc3xxx_create_attr(
		&(mc3xxx_init_info.platform_diver_addr->driver));
	if (err) {
		ACC_PR_ERR("create attribute err = %d\n", err);
		goto exit_create_attr_failed;
	}

	ctl.open_report_data = mc3xxx_open_report_data;
	ctl.enable_nodata = mc3xxx_enable_nodata;
	ctl.batch = mc3xxx_batch;
	ctl.flush = mc3xxx_flush;
	ctl.set_delay  = mc3xxx_set_delay;
	ctl.is_report_input_direct = false;
	ctl.is_support_batch = obj->hw->is_batch_supported;
	err = acc_register_control_path(&ctl);
	if (err) {
		ACC_PR_ERR("register acc control path err\n");
		goto exit_kfree;
	}
	data.get_data = mc3xxx_get_data;
	data.vender_div = 1000;
	err = acc_register_data_path(&data);
	if (err) {
		ACC_PR_ERR("register acc data path err= %d\n", err);
		goto exit_kfree;
	}
	s_nInitFlag = MC3XXX_INIT_SUCC;
	return 0;

exit_create_attr_failed:
exit_misc_device_register_failed:
exit_init_failed:
exit_kfree:
	kfree(obj);
	obj = NULL;
exit:
	ACC_PR_ERR("%s: err = %d\n", __func__, err);
	s_nInitFlag = MC3XXX_INIT_FAIL;

	return err;
}

/*****************************************
 *** mc3xxx_i2c_remove
 *****************************************/
static int mc3xxx_i2c_remove(struct i2c_client *client)
{
	int err = 0;

	err = mc3xxx_delete_attr(
		&(mc3xxx_init_info.platform_diver_addr->driver));
	if (err)
		ACC_PR_ERR("mc3xxx_delete_attr fail: %d\n", err);

	#ifdef _MC3XXX_SUPPORT_VPROXIMITY_SENSOR_
	misc_deregister(&mcube_psensor_device);
	#endif
	mc3xxx_i2c_client = NULL;
	i2c_unregister_device(client);
	accel_factory_device_deregister(&mc3xxx_factory_device);
	kfree(i2c_get_clientdata(client));

	return 0;
}


/*****************************************
 *** mc3xxx_remove
 *****************************************/
static int mc3xxx_remove(void)
{
	MC3XXX_power(hw, 0);
	i2c_del_driver(&mc3xxx_i2c_driver);

	return 0;
}

/*****************************************
 *** mc3xxx_local_init
 *****************************************/
static int  mc3xxx_local_init(void)
{
	if (i2c_add_driver(&mc3xxx_i2c_driver)) {
		ACC_PR_ERR("add driver error\n");
		return -1;
	}

	if (s_nInitFlag == MC3XXX_INIT_FAIL)
		return -1;
	return 0;
}

/*****************************************
 *** mc3xxx_init
 *****************************************/
static int __init mc3xxx_init(void)
{
	acc_driver_add(&mc3xxx_init_info);
	return 0;
}

/*****************************************
 *** mc3xxx_exit
 *****************************************/
static void __exit mc3xxx_exit(void)
{
#ifdef CONFIG_CUSTOM_KERNEL_ACCELEROMETER_MODULE
	success_Flag = false;
#endif
}

/*--------------------------------------------------------------------------*/
module_init(mc3xxx_init);
module_exit(mc3xxx_exit);
/*--------------------------------------------------------------------------*/
MODULE_DESCRIPTION("mc3XXX G-Sensor Driver");
MODULE_AUTHOR("Mediatek");
MODULE_LICENSE("GPL");
MODULE_VERSION(MC3XXX_DEV_DRIVER_VERSION);
